c# - 使用 DI 引导包含多种模块类型的 ASP.NET Core 2.1 应用程序
问题描述
开始为多租户 ASP.NET Core 2.1 应用程序构建基础架构,该应用程序将由许多模块组成。
这个想法是使将来要添加的任何模块都可以插入系统,在应用程序启动时注册它们自己所需的依赖项,并在需要时使用已注册的依赖项(其他模块注册的依赖项)。
让我们首先看一下我想象中的示例代码 - 从我的头顶开始。假设我们有某种模块管理器,我们将命名为 - 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);
}
}
让我们暂时停在那里,首先定义模块类型。我认为可以假设每种模块类型可以根据需要具有不同的属性和字段,但是我们必须使它们都派生自基本模块类型。让我们称之为它BaseModule
,这就是我想象的样子:
public abstract class BaseModule
{
// I've currently got no idea on how this method should be defined
// Because it's supposed to register the concrete module's dependencies
// after the module has been instantiated... is that even possible?
// Should it be some kind of a factory method rather than void
// and serve as a lazy initializer? Is that also even possible?
// Something that should make use of IOptions<>?
public virtual void Init()
{
}
// Maybe some post-init code will be needed too
public virtual void PostInit()
{
}
}
然后我们将定义一些具体的模块类型,如下所示:
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
}
}
假设,为了完整起见,我们webHostModuleType
的定义如下:
[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;
}
}
这样我们可以将我们的函数提取到 nuget 包中,然后通过AddXXXService()
在我们的启动中添加来简单地重新利用它们
推荐阅读
- azure - Azure IOT Hub 到本地数据库服务器
- java - 设计编辑文本代码编辑器,行号不可滚动
- excel - VBA:匹配索引函数
- objective-c - 如何在 iOS 二进制文件中打印所有 Objective-C 类的名称?
- flutter - 可以在颤动中绘制像素吗?
- python - 在多个列表上计算 95 个百分位
- cuda - 我可以在 CUDA 设备端代码中使用可变参数函数吗?
- angular - 提交表单时表单验证不起作用
- node.js - 猫鼬聚合不起作用,尝试了不同的方法仍然无法解决问题,甚至不知道是什么问题
- html - Xpath 仅获取第一个文本标记并忽略它之前的中断标记