C# 在MVC中使用DI时的大量控制器构造函数参数列表
我正在开发ASP.NET MVC3解决方案,该解决方案使用autofac的依赖项注入。 我们的控制器是由autofac创建的,所有必需的对象都被正确地传入。这些对象通常包括将域对象转换为MVC(视图)模型的服务、存储库和映射器。因此,控制器构造函数看起来有点像:C# 在MVC中使用DI时的大量控制器构造函数参数列表,c#,asp.net-mvc-3,dependency-injection,autofac,C#,Asp.net Mvc 3,Dependency Injection,Autofac,我正在开发ASP.NET MVC3解决方案,该解决方案使用autofac的依赖项注入。 我们的控制器是由autofac创建的,所有必需的对象都被正确地传入。这些对象通常包括将域对象转换为MVC(视图)模型的服务、存储库和映射器。因此,控制器构造函数看起来有点像: public abcController( ILogger logger, IabcRepository abcRepository, IabcService abcService,
public abcController(
ILogger logger,
IabcRepository abcRepository,
IabcService abcService,
IMapper<AbcDomain, AbcViewModel> abcMapper,
...
)
公共ABC控制器(
ILogger记录器,
我相信,
IabcService abcService,
伊玛珀,
...
)
不幸的是,随着时间的推移,这些构造函数参数列表往往会快速增长。我们的一些控制器现在期望60个或更多参数
我们在这里创建了一些反模式吗
编辑
我应该提到的是,我们试图遵循瘦控制模式。此外,这些参数中的大多数往往是映射器,约占66%。控制方法通常非常简单,并遵循以下模式之一:
- 根据参数调用适当的服务或存储库
- 使用映射器将结果转换为适当的视图模型
- 将视图模型传递到视图
- 从post操作接收模型
- 使用映射器将其转换为适当的域对象
- 使用域对象调用适当的服务或存储库
interface IABCFactory {
ILogger CreateLogger();
IABCRepository CreateRepo();
// .. etc
}
然后,您的构造函数变成:
private ILogger _logger;
public abcController(IABCFactory factory) {
_logger = factory.CreateLogger();
// .. etc
}
注意,您可以将注入到公共属性中。。但这取决于你是否想让外界知道。如果您不想破坏封装,那么您可以选择工厂。60个或更多的参数是很多的 在您的问题中,您提到“.这些对象通常包括服务、存储库和将域对象转换为MVC(视图)模型的映射器…” 你有一个胖控制器(不是任务引擎类型的Thomas),但是一个做得太多的控制器 我寻找的平衡是胖型瘦控制器。伊恩·库珀在这篇文章中谈得很好 您还可以查看诸如哪些参数实际上是交叉切割关注点之类的内容
例如,在我看来,映射和日志记录是交叉关注点,因此您可以潜在地使用动作过滤器来清理控制器 如果这很大程度上取决于创建视图模型,那么这个问题和答案可能会有所帮助 我还要看看曼宁的MVC4。它包括创建自动映射的ActionResult 在我的应用程序中,大多数控制器操作都是一行的。拉取实体并将其传递给自动映射和丰富viewresult,或者接收命令并将其传递给处理该实体的操作结果 Jimmy的这篇博文涵盖了博文的一些方面 基本上,我得到一个域对象(来自repo或其他方法),并返回一个自动映射的视图结果,该结果映射到相应的VM
return AutoMappedView<ExaminationCreateModel>(new Examination ( _assetRepository.Find(assetId)));
返回autompedview(新检查(_assetRepository.Find(assetId));
然后,mapper ViewResult将其传递给enricher(如果发现一个enricher实现了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)));
}
公共虚拟操作结果创建(AddAssetExaminationCommand AddAssetExaminationCommand,ICommandHandler)因此错误仍然存在。如果命令处理程序处理它是有效的,我们重定向到一个成功页面,我真的无法谈论您应该如何重新构建控制器,尽管我同意大多数其他答案-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();
var builder=newcontainerbuilder();
builder.RegisterAggregateService();
builder.Register(/*…*/).As();
builder.Register(/*…*/).As();
builder.Register(/*…*/).As();
builder.Register(/*…*/).As();
RegisterType();
var container=builder.Build();
同样,它也没有涉及到需要如此多依赖项的更大问题,但是如果您只是想简化构造函数和控制器上的属性数量,以便更易于管理,这是Autofac提供的一种策略,可以在这方面提供帮助
您可以使用按属性注入
而不是按构造函数注入
@HamletHakobyan-Hmmm…这可能会起作用,我必须尝试看看它如何与autofac一起工作。我可以看到这个解决方案在单元测试方面的好处。为什么不回答这个问题。autofac还支持动作注入请参见此处。因此,一个操作只需要一个参数就可以这样做。60仍然是很多:)根据一般经验,构造函数的依赖项不应超过5个。60个依赖项是依赖项数量的12倍。您如何测试一个具有60个依赖项的类?您可能会从分组中受益。史蒂文斯代码是我大量架构的基础。它真的清理了我的代码+Jimmy Bogard的一些映射东西,各种自定义ActionResults和一些自定义ModelEnricher工作。你不是刚把一长串参数移出了控制范围吗