有一个C#asp.net核心项目,我看到一些构造函数中注入的接口数量不断增长。它预计在某些情况下可能超过30或40个接口。
一些谷歌搜索让我到Autofac's Aggregate Services。我的问题是,在asp.net核心的DI框架中是否存在等价物以避免将许多接口传递给构造函数?
答案 0 :(得分:2)
正如Evk在评论中提到的,Microsoft.Extensions.DependencyInjection
处的依赖注入容器故意是一个非常简单的容器。如果您需要更强大的功能,则应考虑切换到完整的Fletched DI容器。 ASP.NET Core是为了允许交换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提供的那些高级功能,这实际上非常简单并且很快完成。
答案 1 :(得分:1)
此处有两种选择,因为Microsoft.Extensions.DependencyInjection
中没有与 Autofac Agregate Services 等效的内容。
您可以想象,您只需要创建一个实现接口的类并封装构造函数注入。
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;
}
}
如前所述,您仍然可以使用您喜欢的容器替换Microsoft.Extensions.DependencyInjection实现。
如果是Autofac,您应该按照以下步骤操作:
Autofac.Extensions.DependencyInjection
。您需要修改您的Program类并添加Autofac服务。
public class Program
{
public static void Main(string[] args)
{
// The ConfigureServices call here allows for
// ConfigureContainer to be supported in Startup with
// a strongly-typed ContainerBuilder.
var host = new WebHostBuilder()
.UseKestrel()
.ConfigureServices(services => services.AddAutofac())
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
在Startup类的ConfigureServices方法中
3.1。通过Populate将服务从IServiceCollection注册到ContainerBuilder。
3.2。直接在ContainerBuilder中注册服务。
3.3。建立你的容器。
3.4。使用容器创建一个AutofacServiceProvider并将其返回。
3.5。在Startup类的Configure方法中,您可以选择注册IApplicationLifetime
。ApplicationStopped事件以在app shutdown时处置容器。
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
this.Configuration = builder.Build();
}
public IContainer ApplicationContainer { get; private set; }
public IConfigurationRoot Configuration { get; private set; }
// ConfigureServices is where you register dependencies. This gets
// called by the runtime before the Configure method, below.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add services to the collection.
services.AddMvc();
// Create the container builder.
var builder = new ContainerBuilder();
// Register dependencies, populate the services from
// the collection, and build the container. If you want
// to dispose of the container at the end of the app,
// be sure to keep a reference to it as a property or field.
//
// Note that Populate is basically a foreach to add things
// into Autofac that are in the collection. If you register
// things in Autofac BEFORE Populate then the stuff in the
// ServiceCollection can override those things; if you register
// AFTER Populate those registrations can override things
// in the ServiceCollection. Mix and match as needed.
builder.Populate(services);
builder.RegisterType<MyType>().As<IMyType>();
this.ApplicationContainer = builder.Build();
// Create the IServiceProvider based on the container.
return new AutofacServiceProvider(this.ApplicationContainer);
}
// Configure is where you add middleware. This is called after
// ConfigureServices. You can use IApplicationBuilder.ApplicationServices
// here if you need to resolve things from the container.
public void Configure(
IApplicationBuilder app,
ILoggerFactory loggerFactory,
IApplicationLifetime appLifetime)
{
loggerFactory.AddConsole(this.Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMvc();
// If you want to dispose of resources that have been resolved in the
// application container, register for the "ApplicationStopped" event.
// You can only do this if you have a direct reference to the container,
// so it won't work with the above ConfigureContainer mechanism.
appLifetime.ApplicationStopped.Register(() => this.ApplicationContainer.Dispose());
}
}
答案 2 :(得分:1)
外观模式在大型软件项目中普遍用于此。 DI的外观模式没有解决,因此可能存在很多问题。外观模式处于逻辑层面,您看到的大多数DI示例都是物理的,有时甚至是晚期!使用外观模式,您可以通过在更高级别的外观服务中对逻辑依赖项进行分组来降低控制器的复杂性,并在构造函数中注入该外观服务。使用该Facade服务,您可以委派基础相关依赖项。维基(https://en.wikipedia.org/wiki/Facade_pattern)给出了一个非常好的解释如何和是什么。
这里还有一个很大的警告!如果您开发项目,那么您应该已经对系统进行了很好的组合。将系统划分为子系统。这些子系统可以成为外观服务的良好候选者。如果你不这样做,你的软件就会变得一团糟。
在物理层面,您可以创建聚合服务,但在逻辑层面上,您需要定义一个外观。如果你在项目开始时找到那些门面服务,它将使你的生活变得更加容易,因为那时你将不得不这样做,因为它可能会更难。
nopCommerce是一个很好的例子,如何不这样做。如果他们在开始时使用了立面图案,那么结构将不会像现在这样。
实现这一点的一个例子是c#在这里:https://visualstudiomagazine.com/articles/2013/06/18/the-facade-pattern-in-net.aspx
立面这个词来自哪里?我住在荷兰。在阿姆斯特丹,如果您乘船游览,那么您将拥有漂亮的大房子。你看到的那些房子的正面叫做外墙。它们从外面看起来很漂亮,但在过去,里面看不到多少美丽。
这在IT词汇中意味着什么?门面隐藏着它背后的东西。