有一个C# asp.net核心项目,我看到一些构造器中注入的接口数量在不断增长。预计在某些情况下可能会超过30或40个接口。
通过谷歌搜索,我找到了Autofac's Aggregate Services。我的问题是,在asp.net核心的依赖注入框架中是否有等价物来避免将许多接口传递给构造函数?
发布于 2017-10-28 03:28:49
正如Evk在评论中提到的,Microsoft.Extensions.DependencyInjection的依赖注入容器故意是一个非常简单的容器。如果你需要更强大的特性,你应该考虑切换到一个完整的依赖注入容器。ASP.NET核心被构建为允许交换DI容器,实际上这样做并不太难。Autofac has a guide介绍了如何做到这一点。
话虽如此,Autofac的聚合服务并没有那么神奇。当然,您可以像Autofac一样构建一些东西,并使用Castle DynamicProxy自动实现聚合服务。但是您也可以简单地手动创建这样的聚合服务:
public class MyAggregateService
{
public IFirstService FirstService { get; }
public ISecondService SecondService { get; }
public IThirdService ThirdService { get; }
public IFourthService FourthService { get; }
public MyAggregateService (IFirstService first, ISecondService second, IThirdService third, IFourthService fourth)
{
FirstService = first;
SecondService = second;
ThirdService = third;
FourthService = fourth;
}
}
// then register that in the container
services.AddTransient<MyAggregateService>();
// and depend on it in the controller
public MyController (MyAggregateService aggregateService)
{ … }当然,你必须多写一点,但实际上不会多写太多。如果你可以不使用Autofac提供的那些高级功能,这实际上是非常简单和快速的。
发布于 2017-10-28 03:37:33
这里有两个替代方案,因为在Microsoft.Extensions.DependencyInjection中没有等同于Autofac聚合服务的。
1.为您的接口创建基本的AggregateServices实现。
正如您可以想象的那样,您只需要创建一个实现接口并封装构造函数注入的类。
public class SomeAggregateService
{
public IFirstService FirstService{get;private set;}
public ISecondService SecondService{get;private set;}
public IThirdService ThirdService{get;private set;}
public IFourthService FourthService{get;private set;}
public SomeController(
IFirstService firstService,
ISecondService secondService,
IThirdService thirdService,
IFourthService fourthService)
{
FirstService = firstService;
SecondService = secondService;
ThirdService = thirdService;
FourthService = fourthService;
}
}优点
将实施的
缺点
它仍然代表某种cluttering.
2.用Microsoft.Extensions实现Autofac
如前所述,您仍然可以用自己喜欢的容器替换Microsoft.Extensions.DependencyInjection实现。
在Autofac的情况下,您应该执行以下步骤:
Autofac安装Autofac.Extensions.DependencyInjection.
public class Program { public static void (string[] args) { //此处的ConfigureServices调用允许在具有//强类型ContainerBuilder的启动中支持// ConfigureContainer。var .UseContentRoot(Directory.GetCurrentDirectory()) = WebHostBuilder() .UseKestrel() .ConfigureServices( => services.AddAutofac()) var .UseIISIntegration() .UseStartup() .Build();host.Run();}}启动类3.1的ConfigureServices方法中的
IApplicationLifetime.ApplicationStopped事件注册,以便在应用程序关闭时释放容器。public class Startup { public Startup(IHostingEnvironment env) { var builder = .AddJsonFile($"appsettings.{env.EnvironmentName}.json",ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json",optional: true,reloadOnChange: true) var optional: true) .AddEnvironmentVariables();this.Configuration = builder.Build();} public IContainer ApplicationContainer { get;private set;} public IConfigurationRoot Configuration { get;private set;} // ConfigureServices是注册依赖项的位置。这在下面的Configure方法之前由运行时//调用。公共服务(IServiceCollection IServiceProvider ConfigureServices){ //将服务添加到集合。services.AddMvc();//创建容器构建器。var builder = new ContainerBuilder();//注册依赖项,填充//集合中的服务,并构建容器。如果您想//在应用程序的末尾处理容器,//请确保将对它的引用保留为属性或字段。/请注意,Populate基本上是一个foreach,用于将//集合中的内容添加到Autofac中。如果您在填充之前在Autofac中注册了//内容,那么// ServiceCollection中的内容可以覆盖这些内容;如果您在填充之后注册//,这些注册可以覆盖ServiceCollection中的内容。根据需要进行混合和匹配。Builder.Populate(服务);builder.RegisterType().As();this.ApplicationContainer = builder.Build();//根据容器创建IServiceProvider。return new AutofacServiceProvider(this.ApplicationContainer);} // Configure是添加中间件的位置。这在// ConfigureServices之后调用。如果你需要从容器中解决问题,可以在这里使用IApplicationBuilder.ApplicationServices //。loggerFactory.AddConsole(this.Configuration.GetSection("Logging"));IApplicationLifetime Configure( public void Configure( IApplicationBuilder app,ILoggerFactory loggerFactory,IApplicationLifetime appLifetime) {public void Configure();app.UseMvc();//如果您想要处理已经在//应用容器中解析的资源,请注册ApplicationStopped事件。//只有在直接引用容器的情况下才能执行此操作,//因此无法使用上面的ConfigureContainer机制。appLifetime.ApplicationStopped.Register(() => this.ApplicationContainer.Dispose());} }
优点
缺点
发布于 2018-02-12 17:47:30
外观模式在大型软件项目中普遍用于此目的。外观模式没有在依赖注入中解决,因此可能会存在许多问题。门面模式是在逻辑层面上的,你看到的大多数依赖注入示例都是物理的,而且有时太晚了!使用外观模式,您可以通过在较高级别对外观服务中的逻辑依赖项进行分组并将该外观服务注入到构造函数中来降低控制器的复杂性。有了外观服务,您就可以委托给底层的相关依赖项。维基(https://en.wikipedia.org/wiki/Facade_pattern)给出了一个很好的解释,说明了怎么做和做什么。
这里还有一个很大的警告!如果你开发你的项目,你应该已经很好地组合了你的系统。将您的系统划分为多个子系统。这些子系统可能是外观服务的很好的候选者。如果你不这样做,你的软件可能会变得一团糟。
在物理层,您可以创建聚合服务,但在逻辑层,您需要定义它的外观。如果您在项目开始时发现了这些门面服务,它将使您的工作变得容易得多,而当您在项目结束时不得不这样做时,它可能会困难得多。
nopCommerce就是一个很好的例子,说明了如何不这样做。如果他们在一开始就使用门面模式,那么构造者就不会像现在这样。
下面是一个用c#实现的例子:https://visualstudiomagazine.com/articles/2013/06/18/the-facade-pattern-in-net.aspx
facade这个词从何而来?我住在荷兰。在阿姆斯特丹,如果你乘船往返,你会看到漂亮的大房子。你看到的那些房子的正面叫做立面。它们从外面看起来很漂亮,但在过去,里面没有太多美丽的东西。
这在IT-words中意味着什么?一个门面隐藏了它背后的东西。
https://stackoverflow.com/questions/46981260
复制相似问题