【问题标题】:ASP.NET Core and ViewModelFactoryASP.NET Core 和 ViewModelFactory
【发布时间】:2017-02-11 04:33:51
【问题描述】:

您好,我正在尝试实现 ViewModelFactory“模式”,考虑到当前 IoC 容器的限制,我想知道实现它的最佳方法是什么。

public class UserCreateViewModelFactory
{
     private readonly DbContext db;

     public UserCreateViewModelFactory(DbContext db){ this.db = db;}

     public void Create(CreateUserViewModel viewModel)
     {
          //Creates the user
     }
}

我将上述类轻松注入到我的控制器ctor中。当我需要更多 ViewModelBuilders 时,我会很头疼,所以我想避免两件事:

  1. 注射膨胀剂
  2. 带有注册的膨胀容器

我希望能够在我的控制器上注入 IViewModelFactory,然后像这样使用它:

[HttpGet]
public IActionResult GetUsers(int id)
{
    return View(viewModelFactory.Build<GetUserViewModel>(id));
}

请注意,在调用 Build(T) 时,它必须调用正确的 IViewModelFactory 实现。

我知道 StructureMap 容器支持将具体实现绑定到相应的接口,但我正在尝试提出一个解决方案,而不必向项目添加另一个依赖项。

【问题讨论】:

  • 您能解释一下为什么要抽象视图模型的创建吗?视图模型是 DTO;这是控制器通常应该自行更新的东西。通常没有理由抽象出 DTO 的创建。
  • 想象一下你有复杂的视图模型。直接在控制器动作的主体中允许创建代码是很糟糕的。与您委派服务来执行复杂业务代码的原理相同。我在这里的目标是纤薄的控制器,所以一个动作应该做的很少,比如响应用户请求并返回其结果。
  • 您的应用程序可能会受益于像this one 这样的设计。
  • @steven 我最近阅读了 Jim Bogart 的 Put your controllers on a diet 系列帖子。我已经非常喜欢 CQRS 模式。虽然这是一个很好的模式,但现在我正在通过服务分离我的业务逻辑。由于 aspnet 核心 IoC,我无法应用相同的调解器模式来解决工厂实现。我正在尝试在没有服务定位器反模式的情况下重现 this 的内容。
  • 防止让您的容器强行限制您的设计。设计应该领先,您选择的框架和库应该遵循所选设计,而不是相反。

标签: c# asp.net-core asp.net-core-mvc ioc-container


【解决方案1】:

我认为,如果您有构建视图模型的构建器,那么工厂是额外的抽象层,可以简单地去掉。
因为您在编译时知道创建的视图模型的类型,所以您可以将所需的构建器注入控制器构造函数。

如果你的控制器创建了很多视图模型并且你最终需要注入很多构建器 - 这可以被视为违反单一责任原则的标志。在这种情况下,您需要将控制器的逻辑分离到不同的控制器。

所以我想避免两件事:

注射膨胀剂

  • 将具有臃肿构造函数的类与具有更具体职责的另一个类分开,这需要较少的依​​赖项。
  • 或者根据它们的关系用一个或两个、三个类包装依赖项

带有注册的膨胀容器

  • 这不是问题,因为依赖容器通常设计用于注册应用程序的整个“对象图”

【讨论】:

  • 这也是一个组织问题——因为控制器可以使用其他依赖项。而且这不一定是违反 SRP 的歌曲,它完全取决于域的需求。一个很好的例子是使用 CQRS + Mediator。这个答案不是很有帮助,因为它没有解决问题约束(如指定的那样)。
  • Controller 只是一个类 - 如果类有太多依赖项 - 这是您问题的主要关注点 - 那么它违反了单一责任原则。一个类只需要一个改变的理由,所以如果你的控制器有不同的逻辑来创建不同的视图模型——那么如果某些逻辑需要改变,你需要改变控制器代码。
  • 这根本不是真的。参数的数量并不代表违反 SRP。尽管它可以指示代码异味并且可能违反 SRP,因为代码“可能”做的比它应该做的更多。以 ProductController 为例,它应该负责将视图路由/返回到 Product 实体。也许我有一个允许我以不同方式编辑/显示产品的业务需求。在这种情况下,我的“改变的一个原因”是提供与我的域相关的功能,该功能应该在该控制器中。
  • 如果控制器的职责是路由/返回视图,则需要为以不同方式编辑/显示产品创建视图模型被提取到另一个类(我认为你引入了工厂类的原因)。在这种情况下,您的控制器只需要对工厂类的一个依赖项。
  • 现在你搞定了!您是否意识到,如果我想在我的控制器上注入单个IViewModelFactory,应用程序必须知道如何调用实现正确IViewModelFactory.Build(T) 的适当具体类型?这就像中介模式。默认容器相当简单,不知道如何连接具体类型,然后我尝试扩展IServiceCollection 来扫描程序集并自己注册具体类型。如果我在这条路上成功,我会更新问题。同时,我会感谢任何其他贡献。
【解决方案2】:

经过一段时间的研究,我终于想出了一个很好的解决这个问题的方法。

解决方案基本上是通过IServiceCollection.ConnectImplementations() 扩展方法扩展默认的IoC 功能。

在注册期间,我将搜索我的具体类并将它们与其各自的接口连接(如其他容器)。然后我使用注入了IServiceCollection 并知道应该由哪个具体类构建视图模型的调解器/代理。

我创建的this gist更好地解释了完整的解决方案。

【讨论】:

  • 如果您需要支持多个输入参数来创建视图模型,这将如何工作?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-07
  • 1970-01-01
  • 2017-03-20
  • 2016-06-28
  • 2016-07-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多