C# 使用DI引导包含多个模块类型的ASP.NET Core 2.1应用程序
已开始为多租户ASP.NET Core 2.1应用程序构建基础架构,该应用程序现在和将来将由许多模块组成 这样做的目的是使将来要添加的任何模块都能够插入系统,在应用程序启动时注册它们自己所需的依赖项,并在需要时使用已经注册的依赖项(由其他模块注册的依赖项) 让我们首先从我的头顶上看一下我想象中的示例代码。 假设我们有一个模块管理器,我们将其命名为-C# 使用DI引导包含多个模块类型的ASP.NET Core 2.1应用程序,c#,asp.net-core,dependency-injection,C#,Asp.net Core,Dependency Injection,已开始为多租户ASP.NET Core 2.1应用程序构建基础架构,该应用程序现在和将来将由许多模块组成 这样做的目的是使将来要添加的任何模块都能够插入系统,在应用程序启动时注册它们自己所需的依赖项,并在需要时使用已经注册的依赖项(由其他模块注册的依赖项) 让我们首先从我的头顶上看一下我想象中的示例代码。 假设我们有一个模块管理器,我们将其命名为-ModuleManager public class ModuleManager { // Let's store all of our mo
ModuleManager
public class ModuleManager
{
// Let's store all of our module types here
private List<Type> moduleTypes;
void RegisterModules(Type webHostModuleType, IServiceCollection services)
{
// Find all module dependencies recursively
// (i.e. all modules that are specified in some, let's say 'DependsOn' attribute
// which decorates webHostModuleType)
moduleTypes = FindDependencies();
// Now we need to register all dependencies
foreach (Type moduleType in moduleTypes)
{
services.AddSingleton(moduleType);
}
// ... and we shouldn't forget to register the webHostModuleType too
services.AddSingleton(webHostModuleType);
}
}
然后我们将定义一些具体的模块类型,如下所示:
public class CoreModule : BaseModule
{
// some dependencies that need to be injected...
private readonly IHostingEnvironment hostingEnvironment;
private readonly IDontKnowSomeOtherDependency someOtherDependency;
public CoreModule(IHostingEnvironment hostingEnvironment, IDontKnowSomeOtherDependency someOtherDependency)
{
this.hostingEnvironment = hostingEnvironment;
this.someOtherDependency = someOtherDependency;
}
public override void Init()
{
// Somehow register additional services defined by this module,
// after it's been instantiated
}
}
[DependsOn(typeof(CoreModule))]
public class WebHostModule : BaseModule
{
private readonly ISomeDependencyRegisteredInCoreModule someDependency;
public WebHostModule(ISomeDependencyRegisteredInCoreModule someDependency)
{
this.someDependency = someDependency;
}
public override void Init()
{
// Register some other service based on something from 'someDependency' field
}
}
比如说,为了完整性起见,我们的webHostModuleType
定义如下:
public class CoreModule : BaseModule
{
// some dependencies that need to be injected...
private readonly IHostingEnvironment hostingEnvironment;
private readonly IDontKnowSomeOtherDependency someOtherDependency;
public CoreModule(IHostingEnvironment hostingEnvironment, IDontKnowSomeOtherDependency someOtherDependency)
{
this.hostingEnvironment = hostingEnvironment;
this.someOtherDependency = someOtherDependency;
}
public override void Init()
{
// Somehow register additional services defined by this module,
// after it's been instantiated
}
}
[DependsOn(typeof(CoreModule))]
public class WebHostModule : BaseModule
{
private readonly ISomeDependencyRegisteredInCoreModule someDependency;
public WebHostModule(ISomeDependencyRegisteredInCoreModule someDependency)
{
this.someDependency = someDependency;
}
public override void Init()
{
// Register some other service based on something from 'someDependency' field
}
}
最后,让我们回到模块管理器。现在它应该有另一个方法,在RegisterModules
之后执行,该方法应该以正确的顺序实例化每个模块,然后以正确的顺序调用Init()
和PostInit()
,从CoreModule
开始,以WebHostModule
结束。类似于此:
public void(?) LoadModules()
{
// Sort our modules first so dependencies come in first and webHostModuleType the last
SortEm(moduleTypes);
// Now we need to instantiate them.
// Can't do it manually as all of them might have different constructors
// So need to do it using our service collection
IServiceProvider serviceProvider = serviceCollection.BuildServicePRovider();
foreach (Type moduleType in moduleTypes)
{
BaseModule moduleInstance = serviceProvider.GetRequiredService(moduleType) as BaseModule;
// This is where we can register everything needed by the module instance.
// But can we?
moduleInstance.Init();
moduleInstance.PostInit();
}
// Maybe return the IServiceProvider instance we've built
// so we can return in to the `ConfigureServices` and return to ASP.NET Core from there?
}
正如您所看到的,这种方法提出了许多问题。我走的方向对吗?是否有一种方法可以正确地使用模块的Init()和PostInit()方法注册服务
如果调用BuildServiceProvider()
然后实例化单例实例,我必须将IServiceProvider
实例返回到ConfigureServices()
以便ASP.NET Core可以使用它。如果我没有,它将构建一个新的,然后所有这些单例将再次实例化
但是如果我调用ConfigureServices()
,那么我将无法添加新的服务,这必须在模块实例化之后进行。如果可能的话,方法是什么?有什么意见、想法吗
哇,这样的文字墙,感谢阅读 好吧,还有一个意见,但我们使用与Net Core团队相同的标准广告,创建扩展方法,因此我们只需要添加需要的服务:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddVtaeCommonServices();
services.AddMyServiceMX(Configuration);
services.AddOtherService(Configuration);
return VtaeConfig.ConfigVtae(services);
}
}
例如,使用此扩展方法:
public static class CommonServicesExtension
{
public static IServiceCollection AddVtaeCommonServices(this IServiceCollection services)
{
services.AddNodaDateTime();
services.AddMemoryCache();
services.AddScoped<AuthorizedIpFilter>();
services.AddScoped<HttpContextManager>();
services.AddTransient<ProspectService>();
services.AddTransient<TokenService>();
services.AddTransient<TokenGenerator>();
services.AddTransient<ProspectsRepository>();
services.AddSingleton<UniqueIDGenerator>();
services.AddSingleton<SchedulerService>();
services.AddSingleton<ChecksumService>();
return services;
}
}
公共静态类CommonServicesExtension
{
公共静态IServiceCollection添加VTAECommonServices(此IServiceCollection服务)
{
services.addNodeAdateTime();
services.AddMemoryCache();
services.addScope();
services.addScope();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddTransient();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
返回服务;
}
}
通过这种方式,我们可以将函数提取到nuget包中,然后通过在启动中添加AddXXXService()
简单地重新利用它们