【发布时间】:2021-05-21 15:24:51
【问题描述】:
我有一个旧的Asp.net Core 3.1 应用程序,它使用Kestrel 服务器,我们所有的GET 和POST 调用都可以正常工作。我的遗留应用程序上已经有一堆中间件,我们将每个中间件用于不同的目的,具体取决于端点是什么。
这就是我们的旧应用程序的设置方式,如下所示。我试图通过只保留重要的东西来保持简单。
下面是我们的BaseMiddleware 类,它由我们拥有的许多其他中间件扩展。大约我们有 10 多个中间件扩展 BaseMiddleware 类 -
BaseMiddleware.cs
public abstract class BaseMiddleware {
protected static ICatalogService catalogService;
protected static ICustomerService customerService;
private static IDictionary <string, Object> requiredServices;
private readonly RequestDelegate _next;
public abstract bool IsCorrectEndpoint(HttpContext context);
public abstract string GetEndpoint(HttpContext context);
public abstract Task HandleRequest(HttpContext context);
public BaseMiddleware(RequestDelegate next) {
var builder = new StringBuilder("");
var isMissingService = false;
foreach(var service in requiredServices) {
if (service.Value == null) {
isMissingService = true;
builder.Append(service.Key).Append(", ");
}
}
if (isMissingService) {
var errorMessage = builder.Append("cannot start server.").ToString();
throw new Exception(errorMessage);
}
_next = next;
}
public async Task Invoke(HttpContext context) {
if (IsCorrectEndpoint(context)) {
try {
await HandleRequest(context);
} catch (Exception ex) {
// handle exception here
return;
}
return;
}
await _next.Invoke(context);
}
public static void InitializeDependencies(IServiceProvider provider) {
requiredServices = new Dictionary<string, Object>();
var catalogServiceTask = Task.Run(() => provider.GetService<ICatalogService>());
var customerServiceTask = Task.Run(() => provider.GetService<ICustomerService>());
// .... few other services like above approx 10+ again
Task.WhenAll(catalogServiceTask, landingServiceTask, customerServiceTask).Wait();
requiredServices[nameof(catalogService)] = catalogService = catalogServiceTask.Result;
requiredServices[nameof(customerService)] = customerService = customerServiceTask.Result;
// ....
}
}
ICatalogService 和ICustomerService 是普通接口,其中包含它们的实现类实现的一些方法。
下面是我们扩展BaseMiddleware 的中间件示例之一。所有其他中间件都遵循与以下相同的逻辑 -
FirstServiceMiddleware.cs
public class FirstServiceMiddleware : BaseMiddleware
{
public FirstServiceMiddleware(RequestDelegate next) : base(next) { }
public override bool IsCorrectEndpoint(HttpContext context)
{
return context.Request.Path.StartsWithSegments("/first");
}
public override string GetEndpoint(HttpContext context) => "/first";
public override async Task HandleRequest(HttpContext context)
{
context.Response.StatusCode = 200;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync("Hello World!");
}
}
public static class FirstServiceMiddlewareExtension
{
public static IApplicationBuilder UseFirstService(this IApplicationBuilder builder)
{
return builder.UseMiddleware<FirstServiceMiddleware>();
}
}
下面是我的Startup 类的配置方式-
Startup.cs
private static ILoggingService _loggingService;
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();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services) {
services.AddResponseCompression(options =>
{
options.Providers.Add<GzipCompressionProvider>();
});
services.Configure<GzipCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Fastest;
});
DependencyBootstrap.WireUpDependencies(services);
var provider = services.BuildServiceProvider();
if (_loggingService == null) _loggingService = provider.GetService<ILoggingService>();
//.. some other code here
BaseMiddleware.InitializeDependencies(provider);
}
public void Configure(IApplicationBuilder app, IHostApplicationLifetime lifetime) {
// old legacy middlewares
app.UseFirstService();
// .. few other middlewares here
}
下面是我的DependencyBootstrap类-
DependencyBootstrap.cs
public static class DependencyBootstrap
{
//.. some constants here
public static void WireUpDependencies(IServiceCollection services)
{
ThreadPool.SetMinThreads(100, 100);
var provider = services.BuildServiceProvider();
var loggingService = provider.GetService<ILoggingService>();
// ... some other code here
try
{
WireUp(services, loggingService);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
private static void WireUp(IServiceCollection services, ILoggingService loggingService)
{
// adding services here
services.AddSingleton<....>();
services.AddSingleton<....>();
//....
var localProvider = services.BuildServiceProvider();
if (IS_DEVELOPMENT)
{
processClient = null;
}
else
{
processClient = localProvider.GetService<IProcessClient>();
}
services.AddSingleton<IData, DataImpl>();
services.AddSingleton<ICatalogService, CatalogServiceImpl>();
services.AddSingleton<ICustomerService, CustomerServiceImpl>();
//.. some other services and singleton here
}
}
问题陈述
我最近开始使用 C# 和 asp.net 核心框架。我已经读完了,它看起来像 -
- 我们的旧应用程序没有正确使用
Dependency Injection,因为我们有很多地方使用BuildServiceProvider方法导致该警告。我不知道为什么我们必须这样做。 - 我们真的需要
InitializeDependencies类中的InitializeDependencies方法吗?如果不是,那么我们如何正确初始化依赖项?看起来我们正在尝试在服务器启动期间初始化所有依赖项,以便在调用任何中间件时它们都准备好。如果可能的话,我想保持这种逻辑。
目前我很困惑在 asp.net 核心中使用 DI 的最佳方法是什么,如果我的应用程序做错了,那么我该如何以正确的方式做呢?很长一段时间以来,上面的代码在我们的应用程序中运行良好,但看起来我们可能以完全错误的方式使用DI。
【问题讨论】:
-
除了 Steven 的出色回答之外,您可能还应该查看用于构建管道的内置 Map 扩展。看起来它可以取代您的自定义
IsCorrectEndpoint架构。
标签: c# asp.net-core dependency-injection inversion-of-control