如何减少控制器上注入的依赖项数量

时间:2012-08-20 23:42:12

标签: asp.net-mvc-3 dependency-injection simple-injector entity-framework-4.3.1

我正在使用MVC3,Entity Framework v4.3 Code First和SimpleInjector。我有几个简单的类看起来像这样:

public class SomeThing
{
    public int Id { get; set; }
    public string Name { get; set; }
}

我有另一个看起来像这样的实体:

public class MainClass
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual AThing AThingy { get; set; }
    public virtual BThing BThingy { get; set; }
    public virtual CThing CThingy { get; set; }
    public virtual DThing DThingy { get; set; }
    public virtual EThing EThingy { get; set; }
}

每个Thingy(当前)都有自己的Manager类,如下所示:

public class SomeThingManager
{
    private readonly IMyRepository<SomeThing> MyRepository;

    public SomeThingManager(IMyRepository<SomeThing> myRepository)
    {
        MyRepository = myRepository;
    }
} 

我的主控制器因此如下:

public class MainController
{
    private readonly IMainManager MainManager;
    private readonly IAThingManager AThingManager;
    private readonly IBThingManager BThingManager;
    private readonly ICThingManager CThingManager;
    private readonly IDThingManager DThingManager;
    private readonly IEThingManager EThingManager;

    public MainController(IMainManager mainManager, IAThingManager aThingManager, IBThingManager bThingManager, ICThingManager cThingManager, IDThingManager dThingManager, IEThingManager eThingManager)
    {
        MainManager = mainManager;
        AThingManager = aThingManager;
        BThingManager = bThingManager;
        CThingManager = cThingManager;
        DThingManager = dThingManager;
        EThingManager = eThingManager;
    }

    ...various ActionMethods...
}

实际上,此控制器中注入的依赖项数量是其两倍。它闻起来臭臭的。当你也知道有一个具有全部或大部分相同依赖关系的OtherController时,气味会更糟。我想重构它。

我已经对DI有足够的了解,知道属性注入和服务定位器不是好主意。

我无法拆分我的MainController,因为它是一个单一的屏幕,只需单击一个Save按钮就可以显示和编辑所有这些内容。换句话说,一个post post方法可以保存所有内容(尽管如果它有意义,我可以更改它,只要它仍然是一个Save按钮)。这个屏幕是使用Knockoutjs构建的,如果有所不同,可以使用Ajax帖子进行保存。

我很幽默使用环境语境,但我不肯定这是正确的方法。 我也很乐意使用注入Facade。 我也想知道我是否应该在此时实现Command架构。 (难道上面的所有这些都不会把气味移到其他地方吗?)

最后,并且可能独立于上述三种方法,我应该使用像GetAThings(),GetAThing(id),GetBThings(),GetBThing(id)等显式方法来使用LookupManager。 ? (但是那个LookupManager需要注入几个存储库,或者是一种新类型的存储库。)

除了我的想法之外,我的问题是,重申:重构这段代码以减少注入依赖项的疯狂数量有什么好方法?

3 个答案:

答案 0 :(得分:4)

您是否考虑过使用工作单元设计模式?关于工作单位是a great MSDN的帖子。该文摘录:

  

在某种程度上,您可以将工作单元视为转储所有内容的地方   交易处理代码。工作单位的职责   是:

     
      
  • 管理交易。
  •   
  • 订购数据库插入,删除和更新。
  •   
  • 防止重复更新。在单个使用工作单元对象内部,代码的不同部分可以标记相同的发票
      对象已更改,但工作单元类只会发出一个
      单个UPDATE命令到数据库。
  •   
     

使用工作单元模式的价值在于释放你的其余部分   来自这些问题的代码,以便您可以专注于   商业逻辑。

有几篇关于此的博文,但我发现的最好的一篇是here。此网站herehere引用了其他一些内容。

  

最后,也许独立于上述三种方法,是   我应该用一个单独的,例如,LookupManager显式   GetAThings(),GetAThing(id),GetBThings(),GetBThing(id)等方法,   等等? (但那LookupManager需要几个   注入其中的存储库或新类型的存储库。)

工作单元将能够处理所有这些,特别是如果您能够为大多数数据库处理需求实现通用存储库。您的标记提到您正在使用Entity Framework 4.3吗?

希望这有帮助!

答案 1 :(得分:3)

使用command architecture是一个好主意,因为这会将所有业务逻辑移出控制器,并允许您在不更改代码的情况下添加cross-cutting concerns。但是,这不会解决您constructor over-injection的问题。标准解决方案是将相关的依赖项移动到aggregate service。但是,我同意Mark的观点,你应该看一下unit of work pattern

答案 2 :(得分:0)

我认为你的主要问题是抽象层次太多。您正在使用Entity Framework,因此您已经拥有了一个围绕数据的抽象层,通过Repository添加了两个层(每个实体一个),而Manager接口导致了控制器所依赖的大量接口。它并没有增加很多价值,此外还有YAGNI

我会重构,删除你的存储库和管理器层,并使用'环境上下文'。

然后,查看控制器询问管理器层的查询类型。在这些非常简单的地方,我发现在控制器中直接查询“环境上下文”没有问题 - 这就是我要做的。在它们更复杂的地方,将它重构为一个新的界面,逻辑分组(不一定是每个实体一个)并使用你的IOC。