所以,这是我第一次使用Ninject,一切都很好,直到我遇到一个问题,其中一些服务类需要在构造函数中添加ModelState参数。通过回答这个问题解决了问题:answer on previous question
现在接下来的问题是我有大约40个服务类需要在构造函数中访问ModelState,因此我选择使用泛型,我真的不知道这是否真的合法?还是我必须重做它?
private readonly IServiceFactory<IAccountService, AccountService> service;
public AccountController(IServiceFactory<IAccountService, AccountService> asf)
{
this.service = asf;
}
来自Ninject的RegisterServices:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind(typeof(IServiceFactory<,>)).To(typeof(ServiceFactory<,>));
}
泛型:
public interface IServiceFactory<T, Y> where T : class where Y : class
{
T Create(ModelStateDictionary modelState);
}
public class ServiceFactory<T, Y> : IServiceFactory<T, Y>
where T : class
where Y : class
{
public T Create(ModelStateDictionary modelState)
{
var x = (T) Activator.CreateInstance(typeof (Y), new ModelStateWrapper(modelState));
return x;
}
}
我试图实现的目标是,我想避免创建40个工厂界面和40个因素,以使exatcly成为同样的东西。我的解决方案有效,但它实际上是否支持依赖注入?我真的无法弄明白。
答案 0 :(得分:2)
如果您注意到my answer on your previous question的最后一部分,您也可以直接通过IAccountService
接口将引用传递给ModelState。在这种情况下,这将简化事情,但鉴于您在上一个问题中缺乏背景,不清楚哪个是更好的选择。
public class AccountController : Controller
{
private readonly ILanguageService languageService;
private readonly ISessionHelper sessionHelper;
private readonly IAccountService accountService;
public AccountController(ILanguageService languageService, ISessionHelper sessionHelper, IAccountService accountService)
{
if (languageService == null)
throw new ArgumentNullException("languageService");
if (sessionHelper == null)
throw new ArgumentNullException("sessionHelper");
if (accountService == null)
throw new ArgumentNullException("accountService");
this.languageService = languageService;
this.sessionHelper = sessionHelper;
this.accountService = sessionHelper;
}
[HttpPost]
public ActionResult PostSomething()
{
// Pass model state of the current request to the service.
this.accountService.DoSomething(new ModelStateWrapper(this.ModelState));
}
}
这显然比注入40个工厂更容易,并且会使您的DI配置更简单。
这种方法的唯一缺点(如果你可以称之为),你的IAccountService
界面现在需要传递IValidationDictionary
的实现。这意味着IAccountService
的每个具体实现都必须依赖于ModelState的抽象IValidationDictionary
。如果这是对设计的期望,那就没问题了。
此外,您应该在您的类中放置guard子句,以确保如果DI容器未能提供实例,则将抛出异常。私有字段中的readonly
关键字使得无法更改构造函数之外的那些字段的值。基本上,这两个组合在一起可以保证您将拥有一个在构造函数中初始化的实例,并且在整个对象的生命周期内保持不变。
最后的想法
DI本质上是复杂的。如果您想获得一个好的答案,您应该提供有关该问题的更多背景信息。我知道SO表示你应该发布最低要求的代码,但是由于其复杂性,关于DI的问题通常需要比其他问题更多的回答。
如果我知道您正在尝试解决40个类似服务的问题,或者已经知道使用依赖关系的IAccountService
成员的相关部分(您仍然没有提供),我的答案可能就是有所不同。如果你提供更多的上下文,它仍然可能会改变,但我可以对你想要实现的目标做出很多假设。如果填写空白,这会有很大帮助。
答案 1 :(得分:1)
依赖注入将具体实现映射到抽象/接口,并将该实例传递给依赖类的构造函数。
映射:
kernel.Bind<IServiceAccount>().To<ServiceAccount>();
Depentandt课程:
private readonly IServiceAccount _serviceAccount;
public AccountController(IServiceAccount serviceAccount)
{
_serviceAccount = serviceAccount;
}
在您的情况下,您似乎应该重新考虑您的服务工作方式,除非您不为您使用的每项服务创建工厂,您是否需要构造函数上的模型状态?,也许您可以将其更改为如果您的工作流程不允许无效状态抛出异常,则在您使用它时服务内部的方法。
如果您需要使用运行时值来创建服务实例,则需要为其创建工厂。
public class ValidServiceAccount : IServiceAccount
{}
public class InvalidServiceAccount : IServiceAccount
{}
public interface IServiceAccountFactory
{
IServiceAccount Create(bool modelState);
}
public class ServiceAccountFactory : IServiceAccountFactory
{
public IServiceAccount Create(bool modelState)
{
return modelState ? new ValidServiceAccount() : new InvalidServiceAccount();
}
}
而不是在yoru控制器构造函数上传递IServiceAccount而是传递一个IServiceAccountFactory,您可以看到这可以轻松地为您的项目增加很多复杂性,但是当您真正需要灵活性时,它确实会得到回报。
注意:在asp.net MVC项目中你应该得到ninject控制器工厂,所以它会为你处理构造函数(我认为这个包叫做Ninject.MVC)。