如何在 Startup 正确解析服务?

在 .NET Core 应用中,通常需要在 Startup 注册和解析服务(resolve services),即在ConfigureServices注册服务,在 ConfigureServicesConfigure中解析服务。例如有以下的场景:我有一个基于 ASP.NET Core 3.1 的应用程序,   在应用程序启动期间,要在 ConfigureServices(IServiceCollection services) 中注册服务。 但是在配置服务期间,想基于设置启动应用程序。

代码如下:

public void ConfigureServices(IServiceCollection services) 
{ 
   // Reister the setting provider 
   services.AddSingleton<IAppSetting, AppSettings>(); 
   // Create a resolver which is WRONG!! 
   var resolver = services.BuildServiceProvider(); 
   var setting = resolver.GetService<IAppSetting>(); 
   List<string> allowedCors = new List<string>() 
   { 
        setting.MainUrl.ToString() 
   }; 
   if (setting.HasCustomAssetsUri) 
   { 
       allowedCors.Add(settings.AssetsUrl.ToString()); 
   } 
   if (settings.HasCustomPhotosUri) 
   { 
       allowedCors.Add(settings.PhotosUrl.ToString()); 
   } 
   // More services 
}

如上面的代码中看到的,注册了IAppSetting,我想立即获得当前配置,通过调用 services.BuildServiceProvider() 创建一个新的解析器,然后可以使用它来创建IAppSetting的实例。

我的AppSetting实现依赖于同时注册的 AppDbContext。  以下是AppSetting

public class AppSetting : IAppSetting 
{ 
   private AppDbContext context; 
   public AppSetting(AppDbContext context) 
   { 
       this.context = context; 
   } 
   // methods... 
}

调用BuildServiceProvider()将导致创建单例服务的其他副本。 所以要尽量避免这样做。那么如何在 ConfigureServices() 方法中正确注册然后解析IAppSetting,以便可以使用它来访问数据库中的设置呢?

这里我们可以使用 DI 服务配置功能,参考官方文档

所以上述代码中,对于 DI 配置CORS,可将所有逻辑移至配置委托中,如下代码所示:

//... 
// Reister the setting provider 
services.AddScoped<IAppSetting, AppSettings>();  
//configure CORS options using DI 
services.AddOptions<CorsOptions>() 
   .Configure<IServiceScopeFactory>((options, sp) => { 
       using(var scope = sp.CreateScope()) { 
           IAppSetting settings = scope.ServiceProvider.GetRequiredService<IAppSetting>(); 
           List<string> allowedCors = new List<string>() { 
                setting.MainUrl.ToString() 
           }; 
           if (setting.HasCustomAssetsUri) { 
               allowedCors.Add(settings.AssetsUrl.ToString()); 
           } 
           if (settings.HasCustomPhotosUri) { 
               allowedCors.Add(settings.PhotosUrl.ToString()); 
           } 
           options.AddPolicy("AllowSubDomainTraffic", builder => { 
               builder.WithOrigins(allowedCors.ToArray()) 
                      .AllowAnyHeader() 
                      .AllowAnyMethod(); 
           }); 
       } 
   }); 
   //...

这样,构建服务便会推迟到实际需要时才进行。

如果是在 Configure 方法,如何获取配置呢?可以通过在方法中注入IServiceProvider,即 Configure(IServiceProvider serviceProvider),实现代码示例如下:

//... 
using (var scope = serviceProvider.CreateScope()) 
{ 
    var settings = scope.ServiceProvider.GetRequiredService<IAppSetting>();  
} 
//...

全文完

《如何在 Startup 正确解析服务?》的相关评论

发表评论

必填项已用 * 标记,邮箱地址不会被公开。