项目
版本

Autofac ASP.NET Core 集成

ASP.NET Core(以前称为 ASP.NET 5)改变了以前的依赖注入框架如何集成到 ASP.NET 执行中。以前,每个功能(如 MVC、Web API 等)都有自己的 “依赖解析器” 机制,只是稍微不同的集成方式。ASP.NET Core 引入了符合规范的容器机制,通过 Microsoft.Extensions.DependencyInjection ,包括统一的请求生命周期范围、服务注册等功能。

此外,从 ASP.NET Core 3.0 开始,有一个通用的应用托管机制,可用于非 ASP.NET Core 应用。

此页面解释了 ASP.NET Core 和通用.NET Core 托管的集成。 如果您正在使用 ASP.NET 经典,请参阅 ASP.NET 经典集成页面

如果您使用的是 .NET Core 但不使用 ASP.NET Core(或不使用通用托管),则可以查看 此处有一个更简单的示例 ,展示这种集成。

快速入门

  • 从 NuGet 引用 Autofac.Extensions.DependencyInjection 包。
  • Program.Main 方法中,将托管机制附加到 Autofac。(参见下面的示例。)
  • Startup 类的 ConfigureServices 方法中,使用其他库提供的扩展方法向 IServiceCollection 注册内容。
  • Startup 类的 ConfigureContainer 方法中,直接向 Autofac 的 ContainerBuilder 注册内容。

IServiceProvider 会自动为您创建,您只需进行注册即可。

ASP.NET Core 1.1 - 2.2

这个示例展示了ASP.NET Core 1.1 - 2.2的用法,您在 WebHostBuilder 上调用 services.AddAutofac() 。**这不是用于 ASP.NET Core 3+**或.NET Core 3+的通用托管支持——ASP.NET Core 3 要求您直接指定服务提供者工厂,而不是将其添加到服务集合中。

public class Program { public static void Main(string[] args) { // ASP.NET Core 1.1 - 2.2: // ConfigureServices调用允许Startup使用带有强类型ContainerBuilder的ConfigureContainer方法。 // AddAutofac()是一个便利方法,用于services.AddSingleton<IServiceProviderFactory<ContainerBuilder>>(new AutofacServiceProviderFactory()) var host = new WebHostBuilder() .UseKestrel() .ConfigureServices(services => services.AddAutofac()) .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup<Startup>() .Build(); host.Run(); } }
C#

这个示例展示了ASP.NET Core 1.1 - 2.2的用法,您从 ConfigureServices(IServiceCollection services) 委托中返回一个 IServiceProvider 。**这不是用于 ASP.NET Core 3+**或.NET Core 3+的通用托管支持——ASP.NET Core 3 已弃用从 ConfigureServices 返回服务提供者的功能。

public class Startup { public Startup(IHostingEnvironment env) { // ASP.NET Core 3.0中,env将是IWebHostEnvironment,不再是IHostingEnvironment。 var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); this.Configuration = builder.Build(); } public IConfigurationRoot Configuration { get; private set; } public ILifetimeScope AutofacContainer { get; private set; } // ConfigureServices是在这里注册依赖项并返回一个实现AutofacServiceProvider的IServiceProvider。 // 这是旧的方式,不推荐,并且在ASP.NET Core 3.0+中不受支持。 public IServiceProvider ConfigureServices(IServiceCollection services) { // 向集合中添加服务。 services.AddOptions(); // 创建一个ContainerBuilder并注册依赖项。 var builder = new ContainerBuilder(); // 在将内容添加到Autofac之前,先填充IServiceCollection的服务描述符,这样Autofac的注册可以根据需要覆盖服务集合中的内容。 builder.Populate(services); // 直接向Autofac注册自己的内容。 builder.RegisterModule(new MyApplicationModule()); AutofacContainer = builder.Build(); // 这将作为应用程序的服务提供者使用! return new AutofacServiceProvider(AutofacContainer); } // Configure是在这里添加中间件。如果需要从容器中解析内容,您可以在这里使用IApplicationBuilder.ApplicationServices。 public void Configure( IApplicationBuilder app, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(this.Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseMvc(); } }
C#

ASP.NET Core 3.0+和通用托管

**在 ASP.NET Core 3.0 中,托管方式发生了变化,需要不同的集成。**您不能再从 ConfigureServices 返回 IServiceProvider ,也不能将服务提供者工厂添加到服务集合中。

这是针对 ASP.NET Core 3+和.NET Core 3+的通用托管支持:

public class Program { public static void Main(string[] args) { // ASP.NET Core 3.0+: // UseServiceProviderFactory调用将Autofac提供程序附加到通用托管机制。 var host = Host.CreateDefaultBuilder(args) .UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureWebHostDefaults(webHostBuilder => { webHostBuilder .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup<Startup>(); }) .Build(); host.Run(); } }
C#

Startup 类

在您的 Startup 类(适用于所有 ASP.NET Core 版本)中,然后使用 ConfigureContainer 访问 Autofac 的 ContainerBuilder 并直接向 Autofac 注册内容。

public class Startup { public Startup(IConfiguration configuration) { // 在ASP.NET Core 3.x中,使用`Host.CreateDefaultBuilder`(如上面的Program.cs片段所示)会根据您的appsettings.json和环境变量为您设置一些配置。有关详细信息,请参阅"Remarks":https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.host.createdefaultbuilder this.Configuration = configuration; } public IConfiguration Configuration { get; private set; } public ILifetimeScope AutofacContainer { get; private set; } // ConfigureServices是在这里注册依赖项。此方法会在下面的ConfigureContainer方法之前被调用。 public void ConfigureServices(IServiceCollection services) { // 向集合中添加服务。不要构建或返回任何IServiceProvider,否则ConfigureContainer方法不会被调用。不要在这里为Autofac创建ContainerBuilder,也不要调用builder.Populate() - 这将在AutofacServiceProviderFactory中为您完成。 services.AddOptions(); } // ConfigureContainer是在这里直接与Autofac注册内容。此方法在ConfigureServices之后运行,因此这里的内容将覆盖在ConfigureServices中注册的内容。 // 不要构建容器;工厂会为您处理它。 public void ConfigureContainer(ContainerBuilder builder) { // 在这里直接向Autofac注册自己的内容。不要调用builder.Populate(),这将在AutofacServiceProviderFactory中为您完成。 builder.RegisterModule(new MyApplicationModule()); } // Configure是在这里添加中间件。此方法在ConfigureContainer之后运行。如果需要从容器中解析内容,您可以在这里使用IApplicationBuilder.ApplicationServices。 public void Configure( IApplicationBuilder app, ILoggerFactory loggerFactory) { // 如果出于某种原因,您需要引用已构建的容器,可以使用GetAutofacRoot方便方法。 this.AutofacContainer = app.ApplicationServices.GetAutofacRoot(); loggerFactory.AddConsole(this.Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseMvc(); } }
C#

配置方法命名约定

ConfigureConfigureServicesConfigureContainer 方法都支持基于 IHostingEnvironment.EnvironmentName 的环境特定命名约定。默认情况下,名称分别为 ConfigureConfigureServicesConfigureContainer 。如果您想要环境特定的设置,可以在 Configure 部分后面加上环境名,如 ConfigureDevelopmentConfigureDevelopmentServicesConfigureDevelopmentContainer 。如果没有匹配环境的方法,则使用默认方法。

这意味着您不一定需要使用 Autofac 配置 来在开发和生产环境中切换配置;您可以通过 Startup 中的程序性设置来实现。

public class Startup { public Startup(IHostingEnvironment env) { // 执行启动时的类似操作,如读取配置。 } // 如果没有环境特定的方法,这是默认的。 public void ConfigureServices(IServiceCollection services) { // 向服务集合中添加内容。 } // 只有当环境为Development时才会被调用。如果没有这个方法,默认的ConfigureServices将不会自动调用。 public void ConfigureDevelopmentServices(IServiceCollection services) { // 添加只对开发环境有用的事项到服务集合中。 } // 如果您没有特定环境的方法,这将是默认选择。 public void ConfigureContainer(ContainerBuilder builder) { // 向 Autofac ContainerBuilder 添加内容。 } // 此配置仅在您的环境为生产环境时被调用。如果调用了此配置方法,默认的 ConfigureContainer 方法将不会被自动调用。 public void ConfigureProductionContainer(ContainerBuilder builder) { // 向 ContainerBuilder 中添加仅适用于生产环境的内容。 } // 如果您没有特定环境的方法,这将是默认选择。 public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { // 设置应用程序 } // 此配置仅在您的环境为 staging 环境时被调用。如果调用了此配置方法,默认的 Configure 方法将不会被自动调用。 public void ConfigureStaging(IApplicationBuilder app, ILoggerFactory loggerFactory) { // 为 staging 环境设置应用程序. } }
C#

这是 ASP.NET Core 应用程序主持的一项功能 - 它不是 Autofac 的行为。在 ASP.NET Core 中,启动过程是由 StartupLoader 类 负责定位需要调用的方法的。如果你想更深入地了解这是如何工作的,可以查看那个类。

依赖注入钩子

ASP.NET 经典集成 不同,ASP.NET Core 是专门为依赖注入设计的。这意味着如果你试图了解如何将服务注入到 MVC 视图( 这里 有更多说明),现在这些都由 ASP.NET Core 控制并文档化了,你只需要按照上面的说明设置你的服务提供者,而无需做任何特定于 Autofac 的操作。

以下是一些有关 ASP.NET Core 集成的链接,它们提供了关于 DI 整合的特定见解:

从 ASP.NET 经典迁移时的主要差异

如果你已经使用过其他 ASP.NET 集成 ,你可能会对迁移 ASP.NET Core 时的关键差异感兴趣。

  • 使用 InstancePerLifetimeScope 替代 InstancePerRequest。 在以前的 ASP.NET 集成中,你可以将依赖项注册为 InstancePerRequest,这将确保每个 HTTP 请求只创建一个依赖项实例。这之所以可行,是因为 Autofac 负责设置 “每个请求的生命周期作用域” 。随着 Microsoft.Extensions.DependencyInjection 的引入,创建每个请求和其他子生命周期作用域现在是框架提供的符合规范的容器的一部分,因此所有子生命周期作用域都被平等对待 - 没有特殊的“请求级别作用域”。相反,你应该将依赖项注册为 InstancePerLifetimeScope,而不是 InstancePerRequest,以获得相同的行为。请注意,如果你在 Web 请求期间创建自己的生命周期作用域,那么在这些子作用域中会得到一个新的实例。
  • 不再有 DependencyResolver。 其他 ASP.NET 集成机制需要在多个位置设置基于 Autofac 的自定义依赖注入解析器。现在通过 Microsoft.Extensions.DependencyInjectionStartup.ConfigureServices 方法,只需返回 IServiceProvider,即可实现“魔法”。在控制器、类等中,如果需要手动进行服务定位,请获取 IServiceProvider
  • 没有特殊的中间件。 在以前的 OWIN 集成 <owin> 中,需要注册一个特殊的 Autofac 中间件来管理请求生命周期。现在 Microsoft.Extensions.DependencyInjection 承担了繁重的工作,所以无需额外的中间件进行注册。
  • 不再需要手动注册控制器。 以前,为了使 DI 工作,你需要将所有控制器注册到 Autofac 中。现在 ASP.NET Core 框架会自动将所有控制器通过服务解析,所以你不再需要这样做。
  • 不再需要通过依赖注入扩展来调用中间件。OWIN 集成 <owin> 中,有诸如 UseAutofacMiddleware() 的扩展,允许将 DI 引入到中间件。现在,通过组合自动注入构造参数和动态解析到中间件的 Invoke 方法的参数,这一切都自动完成。ASP.NET Core 框架处理所有这些。
  • MVC 和 Web API 是一回事。 以前,根据你是使用 MVC 还是 Web API,会有不同的方式来接入 DI。在 ASP.NET Core 中,这两个东西合并在一起,所以只需要设置一个依赖注入解析器,维护一个配置文件即可。
  • 控制器不会从容器中解析;只有控制器构造函数参数会被解析。 这意味着控制器生命周期、属性注入和其他事情都不会由 Autofac 管理,而是由 ASP.NET Core 管理。你可以使用 AddControllersAsServices() 来改变这一点,下面会详细讨论。

控制器作为服务

默认情况下,ASP.NET Core 会从容器解析控制器的 参数,但并不会实际从容器中解析 控制器本身。这通常不是问题,但它意味着:

  • 控制器 的生命周期由框架而非请求生命周期管理。
  • 控制器构造函数参数 的生命周期由请求生命周期管理。
  • 你可能在注册控制器时做的特殊绑定(如设置属性注入)将不起作用。

要更改这一点,当您使用服务集合注册 MVC 时,可以指定 AddControllersAsServices()。这样当服务提供者工厂调用 builder.Populate(services) 时,将自动将控制器类型注册到 IServiceCollection 中。

public class Startup { // 省略其他无关内容... public void ConfigureServices(IServiceCollection services) { // 将控制器作为服务注册,以便它们能够被解析。 services.AddMvc().AddControllersAsServices(); } public void ConfigureContainer(ContainerBuilder builder) { // 如果你想在注册完成后设置控制器的属性注入,可以在这里重写控制器注册。 builder.RegisterType<MyController>().PropertiesAutowired(); } }
C#

有关更详细的教程,包括步行游览,请参阅 Filip Woj 博客上的文章。那里的一位评论者还发现了一些关于 RC2 如何处理控制器作为服务的更改 见此处

多租户支持

由于 ASP.NET Core 对生成请求生命周期作用域的急切性,它使得多租户支持无法直接工作。有时用于租户识别的常见 IHttpContextAccessor 也可能来不及设置。为此,添加了名为 Autofac.AspNetCore.Multitenant 的包来修复这个问题。

启用多租户支持:

  • 添加对 Autofac.AspNetCore.Multitenant 包的引用。
  • Program.Main 中创建 Web 主机时,包含一个调用 UseServiceProviderFactory 扩展,并使用 AutofacMultitenantServiceProviderFactory。提供一个回调,用于配置您的租户。
  • Startup.ConfigureServicesStartup.ConfigureContainer 中,注册那些不会特定于租户的东西,这些属于 根容器
  • 在回调(例如 Startup.ConfigureMultitenantContainer)中构建多租户容器。

以下是 Program.Main 中的部分示例代码:

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(); } }
C#

Startup 类的结构如下:

public class Startup { // 省略其他无关内容... public void ConfigureServices(IServiceCollection services) { // 这些都将进入根容器,且不特定于租户。 services.AddMvc(); // 这添加了必要的中间件到根容器,对于多租户工作是必需的。 services.AddAutofacMultitenantRequestServices(); } public void ConfigureContainer(ContainerBuilder builder) { // 这些都将进入根容器,且不特定于租户。 builder.RegisterType<Dependency>().As<IDependency>(); } public static MultitenantContainer ConfigureMultitenantContainer(IContainer container) { // 这是多租户部分。在这里设置你的租户特定设置。 var strategy = new MyTenantIdentificationStrategy(); var mtc = new MultitenantContainer(strategy, container); mtc.ConfigureTenant("a", cb => cb.RegisterType<TenantDependency>().As<IDependency>()); return mtc; } }
C#

使用子作用域作为根

在复杂应用中,你可能希望将服务划分开来,让根容器在整个应用程序的不同部分之间共享,但使用托管部分(如 ASP.NET Core)的子生存期上下文。例如:

标准 ASP.NET Core 集成和通用托管应用程序支持中,你可以使用 AutofacChildLifetimeScopeServiceProviderFactory 替换标准 AutofacServiceProviderFactory。这允许你在特定命名生存期上下文中附加配置操作,而不是构建好的容器。

public class Program { public static async Task Main(string[] args) { // 创建根容器并注册全局依赖项 var containerBuilder = new ContainerBuilder(); builder.RegisterType<SomeGlobalDependency>() .As<ISomeGlobalDependency>() .InstancePerLifetimeScope(); var container = containerBuilder.Build(); // 使用ServiceProviderFactory方法将Autofac提供程序连接到通用托管机制。 var hostOne = Host .CreateDefaultBuilder(args) .UseServiceProviderFactory(new AutofacChildLifetimeScopeServiceProviderFactory(container.BeginLifetimeScope("root-one"))) .ConfigureWebHostDefaults(webHostBuilder => { webHostBuilder .UseContentRoot(AppContext.BaseDirectory) // 每个主机监听不同的URL,它们共享同一个根容器来共享单例事物,但每个都有自己的逻辑根生存期上下文。将事物注册为 `InstancePerMatchingLifetimeScope("root-one")`(上面提供的范围名称) // 将导致只由第一个主机使用的单例。 .UseUrls("http://localhost:5000") .UseStartup<StartupOne>(); }) .Build(); // 使用ServiceProviderFactory方法将Autofac提供程序连接到通用托管机制。 var hostTwo = Host .CreateDefaultBuilder(args) .UseServiceProviderFactory(new AutofacChildLifetimeScopeServiceProviderFactory(container.BeginLifetimeScope("root-two"))) .ConfigureWebHostDefaults(webHostBuilder => { webHostBuilder .UseContentRoot(AppContext.BaseDirectory) // 同样,第二个主机将共享根容器,但有其自己的根生存期上下文 `root-two`。注册为 `InstancePerMatchingLifetimeScope("root-two")` // 的事物将是仅由第二个主机使用的单例。 .UseUrls("http://localhost:5001") .UseStartup<StartupTwo>(); }) .Build(); await Task.WhenAll(hostOne.RunAsync(), hostTwo.RunAsync()) } }
C#

这将改变你的 Startup 类的工作方式 - 你不再直接在 ConfigureContainer 中使用 ContainerBuilder,现在它是 AutofacChildLifetimeScopeConfigurationAdapter

public class StartupOne { // 当运行低于ASP.NET Core 3.0的应用程序时,使用IWebHostEnvironment public Startup(IWebHostEnvironment env) { // 如果需要,请填入... } public void ConfigureServices(IServiceCollection services) { // 服务集合上的常规ConfigureServices注册... } // 对于子生存期上下文的使用!通过适配器注册你的"根"子生存期上下文事物。 public void ConfigureContainer(AutofacChildLifetimeScopeConfigurationAdapter config) { config.Add(builder => builder.RegisterModule(new AutofacHostOneModule())); } public void Configure( IApplicationBuilder app, ILoggerFactory loggerFactory) { // 通常的app配置... } } public class StartupTwo { // 当运行低于ASP.NET Core 3.0的应用程序时,使用IWebHostEnvironment public Startup(IWebHostEnvironment env) { // 如果需要,请填入... } public void ConfigureServices(IServiceCollection services) { // 服务集合上的常规ConfigureServices注册... } // 对于子生存期上下文的使用!通过适配器注册你的"根"子生存期上下文事物。 public void ConfigureContainer(AutofacChildLifetimeScopeConfigurationAdapter config) { config.Add(builder => builder.RegisterModule(new AutofacHostTwoModule())); } public void Configure( IApplicationBuilder app, ILoggerFactory loggerFactory) { // 通常的app配置... } }
C#

如果你不使用服务提供程序工厂,Populate() 方法提供了一个重载,允许你指定应作为"容器"使用的带有标签的子生存期上下文。

.NET Core集成文档还展示了使用子生存期上下文作为根的示例

使用子生存期上下文作为根与多租户支持不兼容。你必须选择一个,不能两者兼得。

示例

Autofac 示例存储库 中有一个演示 ASP.NET Core 集成的示例项目。