这是设置.假设我有一些需要服务实例的动作过滤器:
public interface IMyService
{
void DoSomething();
}
public class MyService : IMyService
{
public void DoSomething(){}
}
Run Code Online (Sandbox Code Playgroud)
然后我有一个需要该服务实例的ActionFilter:
public class MyActionFilter : ActionFilterAttribute
{
private IMyService _myService; // <--- How do we get this injected
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_myService.DoSomething();
base.OnActionExecuting(filterContext);
}
}
Run Code Online (Sandbox Code Playgroud)
在MVC 1/2中,将依赖关系注入动作过滤器是一个痛苦的屁股.最常见的方法是使用自定义操作调用因为在这里可以看到:http://www.jeremyskinner.co.uk/2008/11/08/dependency-injection-with-aspnet-mvc-action-filters/的这种解决方法背后的主要动机是因为以下方法被认为是与容器的草率和紧密耦合:
public class MyActionFilter : ActionFilterAttribute
{
private IMyService _myService;
public MyActionFilter()
:this(MyStaticKernel.Get<IMyService>()) //using Ninject, but would apply to any container
{
}
public MyActionFilter(IMyService myService)
{
_myService = myService;
} …Run Code Online (Sandbox Code Playgroud) c# asp.net-mvc dependency-injection actionfilterattribute asp.net-mvc-3
我听到很多人说使用IoC.Resolve()是一种不好的做法,但我从来没有听说过一个很好的理由(如果它只是测试而不是你可以嘲笑容器,那么你已经完成了).
现在使用Resolve而不是Constructor Injection的优点是你不需要在构造函数中创建具有5个参数的类,并且无论何时你要创建该类的实例,你都不需要提供它.什么
我正在使用Ninject和与nuget一起安装的MVC3扩展.我的内核设置代码位于App_Start/NinjectMVC3.cs文件中.一切都在控制器中运行良好,但我无法弄清楚如何(正确)绑定Global.asax.cs MvcApplication代码中的接口.
我最终使用了一个hack(创建一个返回bootstrap.kernel的公共NinjectMVC3.GetKernel()方法).但是,这将被弃用,必须有一个正确的方法来做到这一点,我没有看到.
这是我的代码:
public class LogFilterAttribute : ActionFilterAttribute
{
private IReportingService ReportingService { get; set; }
public LogFilterAttribute( IReportingService reportingService )
{
this.ReportingService = reportingService;
}
...
}
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters( GlobalFilterCollection filters )
{
filters.Add( new HandleErrorAttribute() );
filters.Add( new LogFilterAttribute() );
}
...
protected void Application_Start()
{
...
RegisterGlobalFilters( GlobalFilters.Filters );
// NOTE hack:
var kernel = NinjectMVC3.GetKernel();
var logger = kernel.Get<ILogger>();
var bw = new BackgroundWork(logger);
Application["BackgroundWork"] = bw; …Run Code Online (Sandbox Code Playgroud) 现在我们使用DI/IOC,当我们需要将额外的参数传递给构造函数时,我们使用工厂类,例如
public class EmailSender
{
internal EmailSender(string toEmail, string subject,String body, ILogger emailLogger)
{.....}
}
public class EmailSenderFactory
{
ILogger emailLogger;
public EmailSenderFactory(ILogger emailLogger)
{
this.emailLogger = emailLogger;
}
public EmailSender Create(string toEmail, string subject, string body)
{
return new EmailSender(toEmail, subject, body, emailLogger);
}
}
Run Code Online (Sandbox Code Playgroud)
现在问题是我们最终创建了一个完整的工厂类,人们并不总是知道使用它们(他们有时会自己创建它们).编写类的最大负面因素是:
public class EmailSender
{
EmailLogger logger = IoC.Resolve<ILogger>();
internal EmailSender(string toEmail, string subject,String body)
{.....}
}
Run Code Online (Sandbox Code Playgroud)
Pro:我们现在可以安全地使用构造函数而无需工厂类Con:我们必须引用Service Locator(我不担心可测试性,它很容易使用模拟容器作为容器的支持服务).
是否有一些重要的原因,为什么我们不应该这样做?
编辑:经过一番思考,我通过拥有一个私有构造函数,并通过嵌套Factory类,我可以将实现和工厂保持在一起,并防止人们不正确地创建类,所以这个问题已经变得有点没有实际意义了.所有关于SL的点都很脏,当然是正确的,所以下面的解决方案让我高兴:
public class EmailSender
{
public class Factory
{
ILogger emailLogger;
public Factory(ILogger emailLogger) …Run Code Online (Sandbox Code Playgroud) c# java dependency-injection inversion-of-control service-locator
我们可以通过实现IDependencyResolver或扩展DefaultControllerFactory来在MVC中进行DI
我曾经认为这两种不同的方法之间差别不大.
但是,我正在完成我的MVC书,它让我实现了自己的ControllerFactory(而不是扩展默认值),而在CreateController方法中,它实际上有:
(IController) DependencyResolver.Current.GetService(targetType);
Run Code Online (Sandbox Code Playgroud)
所以看起来DefaultControllerFactory实际上使用了DependencyResolver
两者之间必须存在差异,我认为这只会让我感到困惑.
问题
1)本书是否让我使用依赖解析器以简化CustomControllerFactory的实现,而实际的DefaultControllerFactory不使用它?
2)我很难理解这两者的目的.我曾经认为只有两种不同的方式来实现DI,但是我越是深入了解我感觉它们完全不同.看起来依赖性解析器是所有控制器实例化的地方
3)是否有最佳做法试图在两者之间做出选择?也许是一些专业和缺点?
编辑:为清楚起见,我决定上传整个CreateController方法:
public IController CreateController(RequestContext requestContext, string controllerName)
{
Type targetType = null;
switch (controllerName)
{
case "Product":
targetType = typeof (ProductController);
break;
case "Customer":
targetType = typeof (CustomerController);
break;
default:
requestContext.RouteData.Values["controller"] = "Product";
targetType = typeof (ProductController);
break;
}
return targetType == null ? null : (IController) DependencyResolver.Current.GetService(targetType);
}
Run Code Online (Sandbox Code Playgroud)