[翻译] Autofac 入门文档

jopen 4年前

原文链接: 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, 您可以继续了解:

来自: http://www.cnblogs.com/dongbeifeng/p/autofac-getting-started.html