项目
版本

Autofac Web Forms 集成

ASP.NET Web Forms 集成需要使用 Autofac.Web NuGet 包

Web Forms 集成提供了代码背后类的依赖注入集成。它还添加了 基于请求的生命周期支持

此页面解释了 ASP.NET 经典 Web Forms 集成。 如果您正在使用 ASP.NET Core,请 查看 ASP.NET Core 集成页面

快速入门

要将 Autofac 与 Web Forms 集成,您需要引用 Web Forms 集成的 NuGet 包,并在 web.config 中添加模块,同时在 Global 应用类上实现 IContainerProviderAccessor

web.config 中添加模块:

<configuration>
  <system.web>
    <httpModules>
      <!-- 这个部分用于IIS6 -->
      <add
        name="ContainerDisposal"
        type="Autofac.Integration.Web.ContainerDisposalModule, Autofac.Integration.Web"/>
      <add
        name="PropertyInjection"
        type="Autofac.Integration.Web.Forms.PropertyInjectionModule, Autofac.Integration.Web"/>
    </httpModules>
  </system.web>
  <system.webServer>
    <!-- 这个部分用于IIS7 -->
    <modules>
      <add
        name="ContainerDisposal"
        type="Autofac.Integration.Web.ContainerDisposalModule, Autofac.Integration.Web"
        preCondition="managedHandler"/>
      <add
        name="PropertyInjection"
        type="Autofac.Integration.Web.Forms.PropertyInjectionModule, Autofac.Integration.Web"
        preCondition="managedHandler"/>
    </modules>
  </system.webServer>
</configuration>

实现 IContainerProviderAccessor

public class Global : HttpApplication, IContainerProviderAccessor
{
  // 保存应用程序容器的提供程序。
  static IContainerProvider _containerProvider;

  // 将被Autofac HttpModules用于解析和注入依赖项的实例属性。
  public IContainerProvider ContainerProvider
  {
    get { return _containerProvider; }
  }

  protected void Application_Start(object sender, EventArgs e)
  {
    // 构建您的应用程序容器并注册依赖项。
    var builder = new ContainerBuilder();
    builder.RegisterType<SomeDependency>();
    // ... 继续注册依赖项...

    // 注册完毕后,使用注册设置容器提供程序。
    _containerProvider = new ContainerProvider(builder.Build());
  }
}

以下各节详细介绍了这些功能的工作原理以及如何使用它们。

在 Web.config 中添加模块

Autofac 通过使用 IHttpModule 实现来管理组件寿命并将其集成到 ASP.NET 管道中。您需要在 web.config 中配置这些模块。

以下片段显示了已配置的模块:

<configuration>
  <system.web>
    <httpModules>
      <!-- 这个部分用于IIS6 -->
      <add
        name="ContainerDisposal"
        type="Autofac.Integration.Web.ContainerDisposalModule, Autofac.Integration.Web"/>
      <add
        name="PropertyInjection"
        type="Autofac.Integration.Web.Forms.PropertyInjectionModule, Autofac.Integration.Web"/>
    </httpModules>
  </system.web>
  <system.webServer>
    <!-- 这个部分用于IIS7 -->
    <modules>
      <add
        name="ContainerDisposal"
        type="Autofac.Integration.Web.ContainerDisposalModule, Autofac.Integration.Web"
        preCondition="managedHandler"/>
      <add
        name="PropertyInjection"
        type="Autofac.Integration.Web.Forms.PropertyInjectionModule, Autofac.Integration.Web"
        preCondition="managedHandler"/>
    </modules>
  </system.webServer>
</configuration>

注意,虽然有两部分分别用于 IIS6 和 IIS7,但强烈建议您同时使用这两个部分。ASP.NET 开发服务器即使目标部署环境是 IIS7 也会使用 IIS6 设置。如果您使用 IIS Express,它将使用 IIS7 设置。

您在那里看到的模块执行了一些有趣的操作:

  • ContainerDisposalModule 让 Autofac 在请求完成后立即处理在请求处理期间创建的任何组件。
  • PropertyInjectionModule 在页面生命周期执行之前将依赖项注入到页面中。还提供了替代的 UnsetPropertyInjectionModule ,它只会将 Web 表单/控件的属性设置为 null 值。 (仅使用一个或另一个,但不能两者都用。)

在 Global.asax 中实现 IContainerProviderAccessor

依赖注入模块期望 HttpApplication 实例支持 IContainerProviderAccessor 。完整的全局应用程序类如下所示:

public class Global : HttpApplication, IContainerProviderAccessor
{
  // 保存应用程序容器的提供程序。
  static IContainerProvider _containerProvider;

  // 将被Autofac HttpModules用于解析和注入依赖项的实例属性。
  public IContainerProvider ContainerProvider
  {
    get { return _containerProvider; }
  }

  protected void Application_Start(object sender, EventArgs e)
  {
    // 构建您的应用程序容器并注册依赖项。
    var builder = new ContainerBuilder();
    builder.RegisterType<SomeDependency>();
    // ... 继续注册依赖项...

    // 注册完毕后,使用注册设置容器提供程序。
    _containerProvider = new ContainerProvider(builder.Build());
  }
}

Autofac.Integration.Web.IContainerProvider 提供了两个有用的属性:ApplicationContainerRequestLifetime

  • ApplicationContainer 是启动时构建的应用程序容器。
  • RequestLifetime 是一个基于应用程序容器的组件 生命周期范围 ,将在当前 Web 请求结束时被销毁。在需要手动依赖解析/服务查找时可以使用它。它包含的组件(除了单例)将是特定于当前请求的(这是 基于请求的生命周期依赖项 在这里解决的地方)。

技巧和窍门

结构化页面和用户控件以进行 DI

为了向 Web 表单页面( System.Web.UI.Page 实例)或用户控件( System.Web.UI.UserControl 实例)注入依赖项,您必须公开允许设置的依赖项作为公共属性。这使 PropertyInjectionModule 可以为您填充这些属性。

确保在应用程序启动时注册所需的依赖项。

var builder = new ContainerBuilder();
builder.RegisterType<Component>().As<IService>().InstancePerRequest();
// ... 继续注册依赖项,然后构建容器提供程序...
_containerProvider = new ContainerProvider(builder.Build());

然后,在页面的代码背后,为所需的依赖项创建公共的 get/set 属性:

// MyPage.aspx.cs
public partial class MyPage : Page
{
  // 这个属性将由PropertyInjectionModule为您设置。
  public IService MyService { get; set; }

  protected void Page_Load(object sender, EventArgs e)
  {
    // 现在您可以使用为您设置的属性。
    label1.Text = this.MyService.GetMessage();
  }
}

同样的公共属性注入过程也适用于用户控件,只需在应用程序启动时注册组件并为依赖项提供公共 get/set 属性即可。

重要的是要注意对于用户控件,只有当控件在页面请求生命周期的预加载阶段由页面创建并添加到 Controls 集合时,才会自动注入属性。动态创建的控件,无论是通过代码还是通过模板(如 Repeater),在这一点上都不会可见,必须手动注入其属性。

手动属性注入

在某些情况下,如在程序性创建用户控件或其他对象时,您可能需要手动在对象上设置属性。为此,您需要:

  • 获取当前应用程序实例。
  • 将其转换为 Autofac.Integration.Web.IContainerProviderAccessor
  • 从应用程序实例中获取容器提供程序。
  • IContainerProvider 获取 RequestLifetime ,并使用 InjectProperties() 方法在对象上注入属性。

在代码中,这看起来像这样:

var cpa = (IContainerProviderAccessor)HttpContext.Current.ApplicationInstance;
var cp = cpa.ContainerProvider;
cp.RequestLifetime.InjectProperties(objectToSet);

请注意,您需要 AutofacAutofac.Integration.Web 命名空间才能使属性注入工作,因为 InjectProperties()Autofac 命名空间中的扩展方法。

通过属性明确注入

在向现有应用程序添加依赖注入时,有时希望区分哪些 Web 表单页面将注入依赖项,哪些不会。Autofac.Integration.Web 中的 InjectPropertiesAttributeAttributedInjectionModule 有助于实现这一目标。

如果您选择使用 AttributedInjectionModule,除非它们标记有特殊属性,否则默认情况下不会自动注入公共属性的依赖项。

首先,从 web.config 文件中删除 PropertyInjectionModule ,然后用 AttributedInjectionModule 替换它:

<configuration>
  <system.web>
    <httpModules>
      <!-- 这部分用于 IIS6 -->
      <add
        name="ContainerDisposal"
        type="Autofac.Integration.Web.ContainerDisposalModule, Autofac.Integration.Web"/>
      <add
        name="AttributedInjection"
        type="Autofac.Integration.Web.Forms.AttributedInjectionModule, Autofac.Integration.Web"/>
    </httpModules>
  </system.web>
  <system.webServer>
    <!-- 这部分用于 IIS7 -->
    <modules>
      <add
        name="ContainerDisposal"
        type="Autofac.Integration.Web.ContainerDisposalModule, Autofac.Integration.Web"
        preCondition="managedHandler"/>
      <add
        name="AttributedInjection"
        type="Autofac.Integration.Web.Forms.AttributedInjectionModule, Autofac.Integration.Web"
        preCondition="managedHandler"/>
    </modules>
  </system.webServer>
</configuration>

配置完成后,页面和控件将不会默认注入依赖项。相反,它们必须标记为 Autofac.Integration.Web.Forms.InjectPropertiesAttributeAutofac.Integration.Web.Forms.InjectUnsetPropertiesAttribute。两者之间的区别:

  • InjectPropertiesAttribute 将始终为页面/控件设置关联到 Autofac 注册的组件的公共属性。
  • InjectUnsetPropertiesAttribute 只有当它们为 null 并且关联的组件已注册时,才会设置页面/控件的公共属性。
[InjectProperties]
public partial class MyPage : Page
{
  // 这个属性将由 AttributedInjectionModule 自动设置。
  public IService MyService { get; set; }

  // ...根据需要稍后使用该属性。
}

通过基页面类进行依赖注入

如果你不想通过模块(例如之前提到的 AttributedInjectionModulePropertyInjectionModule)自动注入属性,可以以更手动的方式集成 Autofac,方法是创建一个在页面请求生命周期的 PreInit 阶段执行手动属性注入的基页面类。

如果只有少数页面需要依赖注入,这样做可能比较合适,而不需要在管道中包含 AttributedInjectionModule(但仍然需要 ContainerDisposalModule)。如果你有超过少量的页面,考虑通过属性进行显式注入可能是有益的。

protected void Page_PreInit(object sender, EventArgs e)
{
  var cpa = (IContainerProviderAccessor)HttpContext.Current.ApplicationInstance;
  var cp = cpa.ContainerProvider;
  cp.RequestLifetime.InjectProperties(this);
}

定制依赖注入模块

如果提供的 属性未设置属性带有注解 的依赖注入模型不适合您的需求,创建自定义注入行为非常容易。只需继承 Autofac.Integration.Web.DependencyInjectionModule,并在 Web.config 中使用结果即可。

要实现的一个抽象成员如下:

protected abstract IInjectionBehavior GetInjectionBehaviorForHandlerType(Type handlerType);

返回的 IInjectionBehavior 可以是预定义的 NoInjectionPropertyInjectionUnsetPropertyInjection 属性之一;也可以是 IInjectionBehavior 接口的自定义实现。

示例

有关 ASP.NET Web 表单集成的示例项目,请参阅 Autofac 示例仓库

在本文档中