【问题标题】:How to reduce number of injected dependencies on controller如何减少对控制器的注入依赖项的数量
【发布时间】:2012-08-20 23:42:11
【问题描述】:

我正在使用 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;
    }
} 

我的 MainController 因此如下:

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,因为它是一个单一的屏幕,需要通过单击一个保存按钮来显示和编辑所有这些内容。换句话说,单个 post 操作方法可以保存所有内容(尽管我愿意更改它,只要它仍然是单个 Save 按钮)。这个屏幕是用 Knockoutjs 构建的,如果有影响的话,它会用 Ajax 帖子保存。

我幽默地使用了环境上下文,但我并不认为这是正确的方法。 我也幽默地使用了注入 Facade。 我也想知道我是否应该在这一点上实现一个命令架构。 (以上这些不都只是将气味转移到其他地方吗?)

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

抛开我的想法,我的问题是,重申一下:重构此代码以减少注入依赖项的疯狂数量的好方法是什么?

【问题讨论】:

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


【解决方案1】:

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

【讨论】:

  • 感谢您的参考。我喜欢这种方法。我将重构为一个聚合服务以满足我的即时需求,然后更深入地了解命令架构和工作单元。
  • @Steven 您能否提供一个示例,说明这如何与 SimpleInjector 一起使用。我一直在cuttingedge.it/blogs/steven/pivot/entry.php?id=95 关注您的示例,这是一种有用的方法。我如何应用外观或聚合服务。
  • @DavidClarke:我不确定要展示什么。看一下工作单元模式;这通常是一个包装存储库的类。为了防止工作单元中的构造函数过度注入,您可以注入一个知道如何解析存储库的工厂。
  • @Steven 那么在使用命令架构时,如何避免构造函数过度注入?似乎每个动作都需要至少一个处理程序,而我的大多数控制器都有超过 5 个动作。 aggregate service described here 似乎并不适用,因为您肯定不应该为已经具有单一职责的命令“[隐藏]新抽象背后的聚合行为”?您的意思是建议使用参数对象吗? (同一链接中提到的另一个概念)。
【解决方案2】:

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

在某种程度上,您可以将工作单元视为一个倾倒所有内容的地方 事务处理代码。工作单位的职责 是:

  • 管理事务。
  • 排序数据库插入、删除和更新。
  • 防止重复更新。在工作单元对象的单一用法中,代码的不同部分可能会标记同一张发票
    对象已更改,但工作单元类只会发出一个
    向数据库发送单个 UPDATE 命令。

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

有几篇关于此的博客文章,但我发现关于如何实现它的最好的一篇是here。还有一些其他的已经从这个网站herehere引用。

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

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

希望这会有所帮助!

【讨论】:

  • Here 是(还)工作单元模式的另一种变体。
  • 我是否只创建一个所有控制器都使用的 UnitOfWork 类?我已经有一个 GenericRepository。该类将引用每个存储库?
  • 是的,这正是它的作用。当从所有不同的存储库中提取项目时,这个单一单元还将管理项目的并发性,并确保它们之间只有 1 个共享上下文。
  • 我最终会得到一个带有构造函数过度注入的 UnitOfWork 类。看来我无论如何都需要聚合服务。
【解决方案3】:

我认为您的主要问题是抽象层太多。您正在使用实体框架,因此您已经有一个围绕数据的抽象层,通过存储库和管理器接口添加了另外两个层(每个实体一个),这导致您的控制器依赖于大量接口。它并没有增加很多价值,此外,YAGNI

我会重构,摆脱您的存储库和管理层,并使用“环境上下文”。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多