Fork me on GitHub
Autofac 入门

Autofac 入门文档

原文链接:http://docs.autofac.org/en/latest/getting-started/index.html

在程序中使用Autofac的基本模式是:

  • 用控制反转(IoC)的思想组织程序。
  • 添加对 Autofac 的引用。
  • 程序启动阶段
    • 创建 ContainerBuilder
    • 注册组件。
    • 生成容器。
  • 程序执行阶段
    • 从容器创建生命周期范围对象(ILifetimeScope接口)。
    • 使用生命周期范围对象解析组件实例。

本文通过一个简单的控制台程序演示这些步骤。

组织程序

控制反转的基本思路是,不让类创建它的依赖项,而是将依赖项通过构造函数传递给它。请参阅 Martin Fowler 这篇解释依赖注入/控制反转的文章。

在本示例中,我们定义一个输出“今天”的日期的类。但是,我们不想让它和 Console 类捆绑在一起。这有两个原因:1,方便测试,2,在没有Console的环境中也能使用。

我们还对输出日期的方式进行抽象,这样就可以随时切换到另一个版本,比如输出“明天”的日期。

代码:

复制代码
using System;

namespace DemoApp
{

  // 此接口将“输出”概念从Console类解耦。
  // 我们只管“输出”,而不关心操作是怎么进行的。
  public interface IOutput
  {
    void Write(string content);
  }
 
  // 这里的实现方式是向Console输出内容。
  // 也可以用其他方式,比如输出到 Debug 和 Trace。
  public class ConsoleOutput : IOutput
  {
    public void Write(string content)
    {
      Console.WriteLine(content);
    }
  }

  // 这个接口将“输出日期”的概念从具体的输出方法中解耦。
  public interface IDateWriter
  {
    void WriteDate();
  }

  // TodayWriter 是这些元素汇合在一起的地方。
  // 它的构造函数有一个 IOutput 参数,通过提供不同的实现类,
  // TodayWriter可以将日期写到不同的地方。
  // 进一步的,在这里WriteDate的实现方式是输出“今天的日期”,
  // 我们可以用另一个类输出其他格式,或者其他日期。
  public class TodayWriter : IDateWriter
  {
    private IOutput _output;

    public TodayWriter(IOutput output)
    {
      this._output = output;
    }

    public void WriteDate()
    {
      this._output.Write(DateTime.Today.ToShortDateString());
    }
  }
}
复制代码

 

现在我们有了组织良好的依赖关系,接下来引入 Autofac。

添加对 Autofac 的引用

首先向项目添加对Autofac的引用。这个示例中,我们只使用 Autofac 的核心部分。其他类型的应用程序可能需要Autofac 集成库。

最简单的方式是使用 NuGet。“Autofac” 程序包包含所有核心功能。

 

 

程序启动阶段

我们在程序启动时创建 ContainerBuilder 对象,然后向它注册组件。组件可以是表达式,.NET 类型, 或者其他暴露服务的代码。组件可以接受其他依赖项。

假设有下面的.net类型:

public class SomeType : IService
{
}

我们有两种使用它的方式:

- 作为这个类型本身, SomeType

- 作为接口, IService

因此, 这里的组件是 SomeType,它暴露的服务是 SomeType 和 IService。

在 Autofac 中,使用 ContainerBuilder 注册组件:

复制代码
// 创建builder
var builder = new ContainerBuilder(); 

// 通常仅通过接口暴露服务
builder.RegisterType<SomeType>().As<IService>(); 

// 但是, 如果同时需要两种服务(不常见),可以用这种方式:
builder.RegisterType<SomeType>().AsSelf().As<IService>();
复制代码

 

在示例程序中,我们要注册全部组件(类)并暴露他们的服务 (接口),以便他们能连接起来。

我们还需要把容器保存起来,以便稍后使用它解析类型。

复制代码
using System;
using Autofac; 

namespace DemoApp
{

  public class Program
  {
    private static IContainer Container { get; set; } 

    static void Main(string[] args)
    {
      var builder = new ContainerBuilder();
      builder.RegisterType<ConsoleOutput>().As<IOutput>();
      builder.RegisterType<TodayWriter>().As<IDateWriter>();
      Container = builder.Build(); 

      // The WriteDate method is where we'll make use
      // of our dependency injection. We'll define that
      // in a bit.
      WriteDate();
    }
  }
}
复制代码

 

现在,容器里注册了全部所需的组件和服务,接下来我们来使用它进行解析。

程序执行阶段

程序执行时,通过从生存范围对象解析并使用组件。

容器本身即是一个生存范围对象,因此可以从容器直接解析类型,但是并不推荐这么做

解析组件时,根据定义的实例范围创建一个新的实例。(解析组件类似于使用new操作符) 有些组件需要清理 (比如实现IDisposable接口) -当生存范围对象被清理时,Autofac 可以同时清理它解析的组件。

由于容器对象的生命周期与应用程序相同,如果从容器直接解析了大量内容, 未处理的组件就会越积越多,从而造成资源泄露。

相反,我们从容器创建一个生存范围对象,然后通过生存范围来解析对象,清理生存范围对象时,从中解析的组件也一同被清理。

(使用 Autofac 集成库时,子范围通常会自动创建。)

对于本示例,“WriteDate” 方法从范围对象获取对象,使用结束时只要清理范围对象。

复制代码
namespace DemoApp
{
  public class Program
  {
    private static IContainer Container { get; set; } 

    static void Main(string[] args)
    {
      // ...the stuff you saw earlier...
    } 

    public static void WriteDate()
    {
      // Create the scope, resolve your IDateWriter,
      // use it, then dispose of the scope.
      using (var scope = Container.BeginLifetimeScope())
      {
        var writer = scope.Resolve<IDateWriter>();
        writer.WriteDate();
      }
    }
  }
}
复制代码

程序的执行过程如下:

  • “WriteDate” 方法向 Autofac 请求 IDateWriter 实例。
  • Autofac 发现 IDateWriter 映射到 TodayWriter ,于是准备创建一个 TodayWriter 实例。
  • Autofac 发现 TodayWriter 的构造函数需要一个IOutput 实例。
  • Autofac 发现 IOutput 映射到 ConsoleOutput,于是创建一个ConsoleOutput实例。
  • Autofac 使用 ConsoleOutput 实例完成 TodayWriter 的创建。
  • Autofac 返回 TodayWriter。

想输出另一个日期时,可以实现另一个 IDateWriter,然后更改启动阶段的注册内容,而不需要更改其他类。这就是控制反转。

注意: 一般而言,普遍认为服务定位是反模式。(see article) 换言之,随处手工创建范围对象,在代码中零散的使用容器对象是不好的方式。通过使用Autofac 集成库 ,可以避免示例代码中的使用方式。相反,内容在一个位置集中解析,也就是在程序的 “最顶层”位置,极少需要进行手工解析。

更进一步

示例演示了如何使用 Autofac, 您可以继续了解:

posted on 2016-01-15 23:24  HackerVirus  阅读(436)  评论(0编辑  收藏  举报