将Autofac集成到应用程序的基本模式是:
本开始向导通过一个简单的控制台程序来向你介绍以上步骤。一旦有了一定的基础,你可以查看wiki的其他部分来了解更多高级用法以及对于WCF, ASP.NET和其它应用类型的集成方法。
控制反转背后的思想是,在类的构造期间传递依赖而不是在应用程序里将这些类绑在一起让类自己创建他们的依赖。如果想了解更多,你可以查看Martin Fowlerclass="OUTFOX_JTR_TRANS_NODE">的一篇解释依赖注入/控制反转的优秀文章。
我们的示例程序中,定义一个输入当前日期的类。然而我们不想将这个类同控制台绑定,因为我们想在以后控制台不可用的条件下也能测试这个类。
我们尽量使用抽象机制来写这个类,这样如果我们想要将其替换成一个输出明天日期的版本的话将会很容易实现。
编写如下代码:
logs_code_hide('15b9ab8f-0507-4258-bf8c-9f7b0b719fe1',event)" src="/Upload/Images/2016052505/2B1B950FA3DF188F.gif" alt="" />using System; namespace DemoApp { // This interface helps decouple the concept of // "writing output" from the Console class. We // don't really "care" how the Write operation // happens, just that we can write. public interface IOutput { void Write(string content); } // This implementation of the IOutput interface // is actually how we write to the Console. Technically // we could also implement IOutput to write to Debug // or Trace... or anywhere else. public class ConsoleOutput : IOutput { public void Write(string content) { Console.WriteLine(content); } } // This interface decouples the notion of writing // a date from the actual mechanism that performs // the writing. Like with IOutput, the process // is abstracted behind an interface. public interface IDateWriter { void WriteDate(); } // This TodayWriter is where it all comes together. // Notice it takes a constructor parameter of type // IOutput - that lets the writer write to anywhere // based on the implementation. Further, it implements // WriteDate such that today's date is written out; // you could have one that writes in a different format // or a different date. public class TodayWriter : IDateWriter { private IOutput _output; public TodayWriter(IOutput output) { this._output = output; } public void WriteDate() { this._output.Write(DateTime.Today.ToShortDateString()); } } }View Code
现在,我们有了一个组织合理的依赖关系,让我们在项目中引入Autofac。
第一步是在你的项目中添加Autofac引用。本例中,我们只引用了核心的Autofac。其它应用程序类型可能需要引用传统的Autofac集成库。
最简单的方式是使用 NuGet。“Autofac” 包 包含我们需要的所有核心功能。
在应用程序启动时,你需要创建一个ContainerBuilder ,并且用它注册你的组件。一个组件是一个表达式、.net类型或者其他暴露一个或多个服务的代码并且这个组件可以接受其他的依赖。
简单来说,一个.net类型像如下这样实现一个接口:
public class SomeType : IService { }
你可以用两种方式处理类型:
1.作为类型本身,SomeType
2.作为接口,IService
在这种情况下,组件是SomeType,它暴露的服务是SomeType
和 IService。
在autofac中,你可以使用ContainerBuilder注册组件。
1 // 创建builder 2 var builder = new ContainerBuilder(); 3 4 // 通常只需要通过接口暴露服务 5 builder.RegisterType<SomeType>().As<IService>(); 6 7 // 然而, 如果你需要两种服务 (不常见),你可以这样 8 builder.RegisterType<SomeType>().AsSelf().As<IService>();
对于我们的这个示例程序,我们需要需要注册所有组件(类)并且暴露它们的服务(接口),以便更好的将它们连接起来。
我们还需要把容器保存起来,以便稍后使用它解析类型。
1 using System; 2 using Autofac; 3 4 namespace DemoApp 5 { 6 public class Program 7 { 8 private static IContainer Container { get; set; } 9 10 static void Main(string[] args) 11 { 12 var builder = new ContainerBuilder(); 13 builder.RegisterType<ConsoleOutput>().As<IOutput>(); 14 builder.RegisterType<TodayWriter>().As<IDateWriter>(); 15 Container = builder.Build(); 16 17 // WriteDate 方法我们将利用依赖注入。一会我们将定义它 18 WriteDate(); 19 } 20 } 21 }
现在,我们有了一个注册了所有组件的以及服务的容器,下面让我们来使用它。
程序执行期间,你需要从作用域中解析并使用注册的组件。
容器本身就是一个作用域, 从技术上来说,你可以从容器中解析对象。然而不推荐这么做.
解析组件时,根据定义的实例作用域创建一个新的实例。 有些组件需要清理 (比如实现IDisposable接口) -当作用域对象被清理时,Autofac 会同时清理它解析的组件。
由于容器作用域应用程序的生命周期中,如果直接从容器解析出很多内容, 很多组件运行结束后会挂起等待释放资源,从而造成资源泄露。
相反,我们从容器创建一个子作用域并通过子作用域来解析对象,当完成解析组件后清理子作用域时,从中解析的组件也一同被清理。
(使用 Autofac 集成库时,子作用域通常会自动创建。因此你不需要关心子作用域的创建过程)
对于本示例,我们将实现“WriteDate” 方法从范围对象获取对象,使用结束时释放作用域。
1 namespace DemoApp 2 { 3 public class Program 4 { 5 private static IContainer Container { get; set; } 6 7 static void Main(string[] args) 8 { 9 // ...the stuff you saw earlier... 10 } 11 12 public static void WriteDate() 13 { 14 // Create the scope, resolve your IDateWriter, 15 // use it, then dispose of the scope. 16 using (var scope = Container.BeginLifetimeScope()) 17 { 18 var writer = scope.Resolve<IDateWriter>(); 19 writer.WriteDate(); 20 } 21 } 22 } 23 }
现在,执行你的程序:
“WriteDate” 方法向 Autofac 请求 IDateWriter 实例。
Autofac 发现 IDateWriter 映射到 TodayWriter ,于是准备创建一个 TodayWriter 实例。
Autofac 发现 TodayWriter 的构造函数需要一个IOutput 实例。
Autofac 发现 IOutput 映射到 ConsoleOutput,于是创建一个ConsoleOutput实例。
Autofac 使用 ConsoleOutput 实例完成 TodayWriter 的创建。
Autofac 返回 TodayWriter。
如果想在应用程序中输出另一个不同的日期时,可以实现一个不同的 IDateWriter接口,然后更改启动阶段的注册内容。你不需要改变其他类。呀!这就是控制反转。
注意: 一般而言,服务定位在很大程度上认为是反模式。(见文章) 也就是说,到处都是手工创建范围对象,在代码中零散的使用容器对象是不好的方式。通过使用Autofac 集成库 ,可以避免示例代码中的使用方式。相反,内容在一个位置集中解析,也就是在程序的 “最顶层”位置,极少需要进行手工解析。当然,如何设计你的应用程序还是由你自己决定。