依赖注入 Autofac入门


Autofac是一款IOC框架,比较于其他的IOC框架,如Spring.NET,Unity,Castle等等所包含的,它很轻量级性能上也是很高的。

本入门通过一个简单的控制台应用来介绍上述步骤. 一旦你学会了这些基础,你可以查看wiki的剩余部分来了解更多高级用法和 集成 WCF, ASP.NET, 和其他应用类型.

一、构建应用

控制反转背后的核心思想是, 我们不再将应用中的类捆绑在一起, 让类自己去 "new up" 他们的依赖, 而是反过来在类的构造方法中将依赖传递进去. 如果你想进一步深入的话, Martin Fowler 有一篇非常好的解释依赖注入/控制反转的文章 .

在我们的示例应用中, 我们将定义一个类来输出当前的日期. 然而, 我们不希望和 Console 绑定, 因为我们想在控制台不可用的情况依然可以测试和使用这个类.

同样,我们会将输出日期的方法定义成抽象的(接口), 这样的话, 如果我们后续想要切换到一个输出 明天 日期的版本, 我们很快就能搞定.

尝试如下代码:

using System;

namespace DemoApp
{
   //这个接口有助于从Console类中去除“写入输出”的概念。我们并不真正“关心”写操作是如何发生的,只是我们可以写。 
  public interface IOutput
  {
    void Write(string content);
  }

  //IOutput接口的这种实现实际上是我们如何写入控制台。 从技术上讲,我们也可以实现IOutput来写入调试或跟踪...或其他任何地方。
  public class ConsoleOutput : IOutput
  {
    public void Write(string content)
    {
      Console.WriteLine(content);
    }
  }

  //这个接口把执行写操作的实际机制的写日期的概念分离出来。 和IOutput一样,这个过程在一个接口后面被抽象出来。
  public interface IDateWriter
  {
    void WriteDate();
  }

  //这个TodayWriter是所有汇聚一起的地方。
  //请注意,它需要一个类型为IOutput的构造函数参数 - 它允许编写者根据实现写入任何地方。 此外,它实现了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 而它暴露的 服务 指的是 SomeTypeIService.

在Autofac中, 你需要通过 ContainerBuilder 注册, 如下:

// 创建你的构建者
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();

      //WriteDate方法是我们将使用依赖注入的地方。 我们将定义一下。
      WriteDate();
    }
  }
}

现在我们已经拥有了一个注册了所有 组件容器 , 并且他们暴露了合适的 服务 . 开始使用它们吧.

四、应用程序执行

在应用程序执行阶段, 你需要充分利用这些刚注册的组件. 你可以从一个 生命周期解析 它们.

容器本身是也是一个生命周期, 从技术角度来说, 你可以直接从Container解析组件. 然而, 我们并不推荐直接这么做.

解析组件时, 根据 定义的实例作用域, 创建一个对象的新实例. (解析一个组件大致相当于调用"new"实例化一个类. 虽然这个概念看上去有点过于简单化了, 但是从类比的角度来说也是合适的). 一些组件需要被释放 (实现``IDisposable``接口) - Autofac会为你在生命周期释放时处理组件的释放.

然而, 容器在应用的生命周期内一直存在. 如果你直接从该容器中解析了太多东西, 应用结束时将会有一堆东西等着被释放. 这是非常不合适的 (很有可能造成"内存泄漏").

因此, 我们可以从容器中创建一个 子生命周期 并从中解析. 当你完成了解析组件, 释放掉子生命周期, 其他所有也就随之被一并清理干净了.

(当使用 Autofac 集成类库 时, 大部分情况下子生命周期创建已经完成了, 因此无需考虑.)

对于我们的示例应用程序, 我们在生命周期内实现"WriteDate"方法并在结束调用后释放它.

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

    static void Main(string[] args)
    {
      // 你之前看到的东西
    }

    public static void WriteDate()
    {

      //创建作用域,解析IDateWriter,使用它,然后处理作用域。
      using (var scope = Container.BeginLifetimeScope())
      {
        var writer = scope.Resolve<IDateWriter>();
        writer.WriteDate();
      }
    }
  }
}

现在当运行程序时...

  • WriteDate 方法创建了一个生命周期, 从中可以解析依赖. 这么做可以避免内存泄漏 - 如果 IDateWriter 或者它的依赖是可被释放的(disposable)的, 那么当生命周期被释放时他们也将被自动释放.
  • WriteDate 方法手动地从生命周期中解析 IDateWriter . (这就是 "服务定位.") 在内部地...
    • Autofac发现 IDateWriter 对应 TodayWriter 因此开始创建 TodayWriter.
    • Autofac发现 TodayWriter 在它构造方法中需要一个 IOutput. (这就是 "构造方法注入.")
    • Autofac发现 IOutput 对应 ConsoleOutput 因此开始创建新的 ConsoleOutput 实例.
    • Autofac使用新的 ConsoleOutput 实例完成 TodayWriter 的创建.
    • Autofac返回完整构建的 TodayWriter 给"WriteDate"使用.
  • 调用 writer.WriteDate() 就是一个全新的 TodayWriter.WriteDate() 因为这就是它所解析出的.
  • Autofac生命周期被释放. 任何从生命周期解析出的可释放对象也都被同时释放.

之后,如果你希望你的应用输出一个不同的日期, 你可以实现另外一个 IDateWriter 然后在应用启动时改变一下注册过程. 你不需要修改任何其他的类. 耶, 这就是控制反转!

注意: 通常来说, 服务定位模式大多情况应被看作是一种反模式 (阅读文章). 也就是说, 在代码中四处人为地创建生命周期而少量地使用容器并不是最佳的方式. 使用 Autofac 集成类库 时你通常不必做在示例应用中的这些事. 这些东西都会在应用的中心,"顶层"的位置得到解决, 人为的处理是极少存在的. 当然, 如何构建你的应用取决于你自身.

五、进一步

这个例子告诉你怎么使用Autofac,但依然有很多你可以做的.

需要帮助?

原文地址:https://www.cnblogs.com/springsnow/p/13851711.html