[翻译] 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 的方式:

- 作为这个类型本身, 也就是声明为 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();
    }
  }
}

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

程序执行阶段

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

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

解析组件时,根据注册时定义的实例范围创建一个新的实例。(解析组件类似于使用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()
    {
      // 创建范围,解析 IDateWriter,
      // 使用 writer,最后清理范围对象。
      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,然后更改启动阶段的注册内容,而不需要更改其他类。这就是控制反转。

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

更进一步

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

 

posted @ 2016-01-15 23:13  东北风!  阅读(3661)  评论(0编辑  收藏  举报