好的,我正在寻找有关依赖注入以及如何使用它的一些特定提示。
基本上我有一个使用Ninject(和Ninject MVC包)的MVC网站。因此,当我创建MVC页面时,我在控制器中使用构造函数注入。这没关系,但IMO有点'难看',但我不喜欢的主要事情是必须将所有注入的存储库传递到其他类中,它似乎有点OTT必须通过6个存储库-8存储库进入静态方法或对象构造函数。
更不用说在我的一些页面上我几乎每个存储库都要工作,因此控制器构成器变得庞大而且不是最容易管理的。
还有其他选项不会让我的代码混乱吗?我真的不想将它们作为单个“设置”对象传递,因为它只是将问题移动到不同的代码行。
我也使用相同的类库来控制台/桌面应用程序。我喜欢在类库中使用DependencyResolver.Current的想法,但是每个人都说这是一个反模式,应该使用构造函数注入。
也许有一个MyProjectDIContext类,它有一个字典,我可以在控制器构造函数中填充注入的类型,然后根据需要将上下文传递给所有方法?
我已经找到了答案,我似乎找不到合适的东西。
答案 0 :(得分:2)
构造函数注入的一个好处是它使设计和可维护性问题更加明显。使用构造函数注入时,很容易看到类具有的依赖项数量,而没有构造函数注入,类仍然具有相同数量的依赖项,但它们被隐藏起来。
您看到的问题称为Constructor Over-injection并且它具有设计气味,因为它表示您违反了Single Responsibility Principle(SRP)。 SRP指导您保持课堂规模小,重点突出,最重要的是:可维护。
还有其他选项不会让我的代码混乱吗?
绝对:制作较小的课程。当我们使用MVC控制器对特定概念的方法进行分组时,MVC控制器通常会变得非常庞大,例如客户'或者'订购'。然而,这意味着控制器是一个不断增长的类,必须针对任何新功能进行更改。这违反了Open/closed Principle,它告诉我们应该努力建立一个系统,我们可以在不触及现有类的情况下插入新功能。
因此,解决方案不是恢复到Service Locator anti-pattern或属性注入,而是创建更小的类来完成一个特定的事情。构造函数注入应该是应用依赖注入的主要方式,即使在类库中也是如此。
答案 1 :(得分:0)
您的控制器似乎正在将存储库转移到其他类。让Ninject代替你提供这些课程:
x86_64
您不必编写public class Controller
{
public Controller(IDependencyFactory dependency) { }
}
public interface IDependencyFactory
{
IDependency CreateDependency();
}
public interface IDependency
{
}
public class Dependency : IDependency
{
public Dependency() { }
}
public class Program
{
public static void Main()
{
var standardKernel = new StandardKernel();
standardKernel.Bind<IDependencyFactory>().ToFactory();
standardKernel.Bind<IDependency>().To<Dependency>();
}
}
的实现,该实现由工厂扩展程序处理。你的IDependencyFactory
- 类将获得Ninject注入的依赖项。
答案 2 :(得分:0)
好吧,我似乎已经找到了一种方法来做到这一点,看起来并不臭!
我曾经想过如果您使用MVC,那么您将通过构造函数获取存储库,这是解决依赖关系的唯一方法。
在完成将可重用代码的部分移动到IoC友好接口及其实现之后,我注意到当您在控制器构造函数中创建引用IMyInterface时,实现类的默认构造函数会运行,您可以拉出其他IoC类,如从那里解决了存储库。
这可能是常识,但它确实使事情更加整洁,并解决了我遇到的问题。
实施例
控制器
public IDefaultTemplateManager DefaultTemplateManager { get; set; }
public DefaultController(IDefaultTemplateManager defaultTemplateManager) {
this.DefaultTemplateManager = defaultTemplateManager;
}
public ActionResult MyAction(FormCollection collection) {
DefaultTemplateManager.Process("MyKeyHere");
View();
}
IDefaultTemplateManager
public interface IDefaultTemplateManager {
ProcessResponse Process(string UniqueKey, DefaultTemplateManagerEditMode DatabaseName, string DefaultTemplateName);
}
DefaultTemplateManager
public class DefaultTemplateManager : IDefaultTemplateManager {
protected IRepository<MyEntity1> MyEntityRepo1 { get; set; }
protected IRepository<MyEntity2> MyEntityRepo2 { get; set; }
protected IRepository<MyEntity3> MyEntityRepo3 { get; set; }
protected IRepository<MyEntity4> MyEntityRepo4 { get; set; }
protected IRepository<MyEntity5> MyEntityRepo5 { get; set; }
protected IRepository<MyEntity6> MyEntityRepo6 { get; set; }
public DefaultTemplateManager(IRepository<MyEntity1> MyEntityRepository1, IRepository<MyEntity2> MyEntityRepository2, IRepository<MyEntity3> MyEntityRepository3, IRepository<MyEntity4> MyEntityRepository4, IRepository<MyEntity5> MyEntityRepository5, IRepository<MyEntity6> MyEntityRepository6, ) {
this.MyEntityRepo1 = MyEntityRepository1;
this.MyEntityRepo2 = MyEntityRepository2;
this.MyEntityRepo3 = MyEntityRepository3;
this.MyEntityRepo4 = MyEntityRepository4;
this.MyEntityRepo5 = MyEntityRepository5;
this.MyEntityRepo6 = MyEntityRepository6;
}
public ProcessResponse Process(string UniqueKey) {
/* Do Work */
}