ASP.NET Core
ASP.NET Core (previously ASP.NET 5) 改變了以前依賴注入框架集成進ASP.NET的方法. 以前, 每個功能 - MVC, Web API, 等. - 都有它自己的 "依賴解析器(dependency resolver)" 機制並且只是'鈎子'鈎住的方式有些輕微的區別. ASP.NET Core 通過 Microsoft.Extensions.DependencyInjection 引入了 conforming container 機制, 包含了請求生命周期作用域, 服務注冊等等的統一概念.
在 ASP.NET Core 3.0, 引入了 "generic app hosting" 機制, 它可以應用在非 ASP.NET Core 應用中.
Autofac
中文官網:https://autofaccn.readthedocs.io/
入門
- Nuget引入 Autofac.Extensions.DependencyInjection 包.
- 在你的 Program.Main 方法內, 將Autofac附加給托管機制.(見下例)
- 在 Startup 類的 ConfigureServices 方法中用其他庫提供的擴展方法注冊東西到 IServiceCollection .
- 在 Startup 類的 ConfigureContainer 方法中直接注冊東西到Autofac ContainerBuilder.
- IServiceProvider 會自動替你創建, 因此你無需做任何事只要 注冊東西 即可.
ASP.NET Core 3.0+generic hosting
ASP.NET Core 1.1 - 2.2 使用方法, 你可以調用 WebHostBuilder 的 services.AddAutofac()
在ASP.NET Core 3.0托管方式發生了變化 並且需要不同的集成方式. 你不能在從 ConfigureServices 中返回 IServiceProvider, 也不能再將你的service provider factory加入到service collection.
下面是ASP.NET Core 3+ 和 .NET Core 3+ generic hosting support的集成方式:
Program類
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup類
添加方法 ConfigureContainer
public void ConfigureContainer(ContainerBuilder builder)
{
// 在這里添加服務注冊
builder.RegisterType<TopicService>();
}
配置方法命名約定
- Configure, ConfigureServices, 和 ConfigureContainer 方法都支持基於你應用中 IHostingEnvironment.EnvironmentName 參數的環境特定命名約定. 默認地, 名稱為 Configure, ConfigureServices, 和 ConfigureContainer.
- 如果你想要環境特定設置, 你可以把環境名稱放在 Configure 部分后面, 類似 ConfigureDevelopment, ConfigureDevelopmentServices, 和 ConfigureDevelopmentContainer. 如果方法並不以匹配的環境名稱顯示, 它會回到默認方法.
這意味着你不必使用 Autofac配置在生產環境和開發環境之間切換; 你可以在 Startup 中以編程形式設置.
public void ConfigureDevelopmentContainer(ContainerBuilder builder)
{
}
public void ConfigureProductionContainer(ContainerBuilder builder)
{
// Add things to the ContainerBuilder that are only for the
// production environment.
}
這是ASP.NET Core應用托管的一個功能,它並不是Autofac的行為. ASP.NET Core中的StartupLoader類 是在應用啟動時定位調用方法的.
控制器作為服務
默認地, ASP.NET Core 會從容器中解析控制器參數,但不會從中解析控制器 . 這並不是個問題但它意味着:
- 控制器 的生命周期歸框架管理, 而非請求生命周期.
- 控制器構造方法參數 歸請求生命周期管理.
- 在控制器注冊時做的特別的連結 (如屬性注入) 將不會生效.
通過在用service collection注冊MVC時指定 AddControllersAsServices() , 你可以改變這個行為. 這么做可以在service provider factory調用 builder.Populate(services) 時自動注冊控制器類型到 IServiceCollection.
public void ConfigureServices(IServiceCollection services)
{
//services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
//或者將Controller加入到Services中,這樣寫上面的代碼就可以省略了
services.AddControllersWithViews().AddControllersAsServices();
}
如果需要在Controller中使用屬性注入,需要在ConfigureContainer中添加如下代碼
//如果需要在Controller中使用屬性注入,需要在ConfigureContainer中添加如下代碼
var controllerBaseType = typeof(ControllerBase);
builder.RegisterAssemblyTypes(typeof(Program).Assembly)
.Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
.PropertiesAutowired();
在Controller中使用
[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
private readonly TopicService _service;
private readonly IServiceProvider _provider;
public TopicService Service { get; set; }
public TestController(TopicService service, IServiceProvider provider)
{
_service = service;
_provider = provider;
}
[HttpGet("{id}")]
public async Task<Result> GetTopics(int id)
{
// 構造函數注入
return await _service.LoadWithPosts(id);
}
[HttpGet("Get/{id}")]
public async Task<Result> GetTopics2(int id)
{
// 屬性注入
return await Service.LoadWithPosts(id);
}
}
這樣就控制器就可以使用服務了。
多租戶支持
由於ASP.NET Core想要早早地生成請求生命周期作用域, 這會導致多租戶支持無法達到開箱即用的效果. 有時用於識別租戶身份的 IHttpContextAccessor , 也無法被及時地構建. Autofac.AspNetCore.Multitenant 包就是用於解決這個問題的.
為了啟用多租戶支持:
- 添加 Autofac.AspNetCore.Multitenant NuGet包引用.
- 在 Program.Main 中構建web host時調用 UseServiceProviderFactory 和 AutofacMultitenantServiceProviderFactory. 提供一個配置租戶的回調.
- 在 Startup.ConfigureServices 和 Startup.ConfigureContainer 中注冊進入 根容器(root container) 的東西(那些非租戶特有的).
- 在回調中 (如
Startup.ConfigureMultitenantContainer
) 構建你的多租戶容器.
下面是Autofac給的一個示例:
public class Program
{
public static async Task Main(string[] args)
{
var host = Host
.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacMultitenantServiceProviderFactory(Startup.ConfigureMultitenantContainer))
.ConfigureWebHostDefaults(webHostBuilder => webHostBuilder.UseStartup<Startup>())
.Build();
await host.RunAsync();
}
}
... Startup 類似這樣:
public class Startup
{
// Omitting extra stuff so you can see the important part...
public void ConfigureServices(IServiceCollection services)
{
// This will all go in the ROOT CONTAINER and is NOT TENANT SPECIFIC.
services.AddMvc();
// This adds the required middleware to the ROOT CONTAINER and is required for multitenancy to work.
services.AddAutofacMultitenantRequestServices();
}
public void ConfigureContainer(ContainerBuilder builder)
{
// This will all go in the ROOT CONTAINER and is NOT TENANT SPECIFIC.
builder.RegisterType<Dependency>().As<IDependency>();
}
public static MultitenantContainer ConfigureMultitenantContainer(IContainer container)
{
// This is the MULTITENANT PART. Set up your tenant-specific stuff here.
var strategy = new MyTenantIdentificationStrategy();
var mtc = new MultitenantContainer(strategy, container);
mtc.ConfigureTenant("a", cb => cb.RegisterType<TenantDependency>().As<IDependency>());
return mtc;
}
}