4

我正在研究使用依赖注入和 autofac 的 ASP.NET MVC3 解决方案。我们的控制器是由 autofac 正确创建的,并且所有需要的对象都被正确传入。这些对象通常包括将域对象转换为 MVC(视图)模型的服务、存储库和映射器。所以控制器构造函数看起来有点像:

public abcController(
        ILogger logger,
        IabcRepository abcRepository,
        IabcService abcService,
        IMapper<AbcDomain, AbcViewModel> abcMapper,
        ...
        )

不幸的是,这些构造函数参数列表往往会很快增长。我们的一些控制器现在需要 60 个或更多参数。

我们是否在这里创建了一些反模式?

编辑

我应该提到我们尝试遵循薄控制器模式。此外,这些参数中的大多数往往是映射器——大约 66%。控制方法通常非常简单,并遵循以下任一模式:

  • 基于参数调用适当的服务或存储库
  • 使用映射器将结果转换为适当的视图模型
  • 将视图模型传递给视图

或者这种模式:

  • 从 post action 接收模型
  • 使用映射器将其转换为适当的域对象
  • 使用域对象调用适当的服务或存储库
4

4 回答 4

8

我真的不能说你应该如何重新设计你的控制器,尽管我同意大多数其他答案——60 个传入参数很多。

Autofac 拥有的聚合服务支持可能有助于减少参数数量而不是依赖项数量。

您可以采用一个包含 60 个属性的聚合参数,而不是直接采用 60 个参数。

您创建一个带有依赖项的接口(只是接口,您实际上不必实现它):

public interface IMyAggregateService
{
  IFirstService FirstService { get; }
  ISecondService SecondService { get; }
  IThirdService ThirdService { get; }
  IFourthService FourthService { get; }
}

然后修改您的控制器以采用该聚合接口:

public class SomeController
{
  private readonly IMyAggregateService _aggregateService;

  public SomeController(
    IMyAggregateService aggregateService)
  {
    _aggregateService = aggregateService;
  }
}

您可以注册聚合服务接口、依赖项和控制器,当您解析控制器时,聚合服务接口将自动为您实现和解析。

var builder = new ContainerBuilder();
builder.RegisterAggregateService<IMyAggregateService>();
builder.Register(/*...*/).As<IFirstService>();
builder.Register(/*...*/).As<ISecondService>();
builder.Register(/*...*/).As<IThirdService>();
builder.Register(/*...*/).As<IFourthService>();
builder.RegisterType<SomeController>();
var container = builder.Build();

同样,它并没有涉及需要那么多依赖项的更大问题,但是如果您只是想简化构造函数和控制器上的属性数量以便更易于管理,这是 Autofac 提供的一种策略来帮助解决这个问题.

查看 wiki 页面了解更多详情。

于 2013-01-25T16:01:07.973 回答
6

60个或更多的参数很多。

在您的问题中,您说“..这些对象通常包括将域对象转换为 MVC(视图)模型的服务、存储库和映射器......”

你有一个胖控制器(不是 Thomas The Task Engine 那种),而是一个做得太多的控制器。

我寻找的平衡是 Fat Model 瘦控制器。伊恩·库珀(Ian Cooper)在这篇博文中谈得很好

您还可以查看诸如哪些参数实际上是横切关注点。

例如,在我看来,映射和日志记录是交叉问题,因此您可能会使用操作过滤器来清理您的控制器。

于 2013-01-24T22:29:34.240 回答
3

(免责声明:这个答案是关于参数列表的大小。它不会减少控制器内的依赖关系)

在这种情况下,您将注入一个工厂。

例如:

interface IABCFactory {
    ILogger CreateLogger();
    IABCRepository CreateRepo();
    // .. etc
}

然后您的构造函数变为:

private ILogger _logger;

public abcController(IABCFactory factory) {
    _logger = factory.CreateLogger();
    // .. etc
}

请注意,您可以注入公共属性。但是否要将其公开给外界取决于您。如果你不想打破封装,那么你会去工厂。

于 2013-01-24T22:18:13.990 回答
3

如果其中很多都归结为创建视图模型,那么这个问题和答案可能会有所帮助。

MVC - 具有多个选择列表的控制器

我还想看看 Manning 的 MVC 4 in Action。它涵盖了创建自动化映射的 ActionResult。

在我的应用程序中,我的大多数控制器操作都是一行。拉出一个实体并将其传递给自动映射并丰富视图结果,或者接收命令并将其传递给处理它的操作结果

Jimmy 的这篇博客文章涵盖了一些 POST 方面 http://lostechies.com/jimmybogard/2011/06/22/cleaning-up-posts-in-asp-net-mvc/

基本上我得到一个域对象(来自 repo 或其他方法)并返回一个映射到适当 VM 的自动映射视图结果。

return AutoMappedView<ExaminationCreateModel>(new Examination ( _assetRepository.Find(assetId)));

映射器 ViewResult 然后将其传递给丰富器(如果发现实现 IModelEnricher。请参阅其他堆栈问题。

返回时,它会作为命令发回,然后命令的处理方式有点像 Bogard post。

    public virtual ActionResult Create(AddAssetExaminationCommand addAssetExaminationCommand, ICommandHandler<AddAssetExaminationCommand> addExaminationHandler) 
    {
        return ProcessForm(
            addAssetExaminationCommand,
            addExaminationHandler,
            RedirectToAction(MVC.OnboardAsset.Examinations.Create()),
            RedirectToAction(MVC.OnboardAsset.Examinations.Index(addAssetExaminationCommand.AssetId)));
    }

如果验证失败,则重定向到 GET 并合并 Modelstate(PRG 模式使用类似的东西,因此错误仍然存​​在。如果它是有效的命令处理程序处理它,我们将重定向到成功页面

于 2013-01-24T22:45:00.907 回答