Autofac 学习简易教程随笔(一)
概念
Autofac 是一款超赞的.NET IoC 容器 . 它管理类之间的依赖关系, 从而使 应用在规模及复杂性增长的情况下依然可以轻易地修改。
那么,什么是IoC(控制反转),以及IoC和DI(依赖注入),已经有大神对这个解释的非常清楚,感兴趣的可以看这个帖:https://www.iteye.com/blog/jinnianshilongnian-1413846
本着多动原则,我这里直接进入step by step环节。本人也建议先按步骤做一遍简单的Autofac Demo项目,然后再看大神对IoC和DI介绍的帖子,会更容易理解。
实践
1、创建一个控制台项目,如下图:
2、引入autofac nuget包,如下图:
3、创建接口IVehicle(交通工具接口)、类Bus(巴士,实现IVehicle接口),代码如下:
IVehicle.cs:
/// <summary> /// 交通工具接口 /// </summary> public interface IVehicle { /// <summary> /// 去哪里 /// </summary> /// <param name="targetAddress">目的地</param> void GoTo(string targetAddress); }
Bus.cs:
/// <summary> /// 大巴 /// </summary> public class Bus : IVehicle, IDisposable { private string _vehicleName { get; } public Bus() { _vehicleName = "大巴"; } public void GoTo(string targetAddress) { Console.WriteLine($"乘 {_vehicleName} 去 {targetAddress}"); } public void Dispose() { Console.WriteLine($"{nameof(Bus)} dispose"); } }
4、创建接口IPassenger(乘客/旅客接口)、类Student(学生,实现IPassenger接口),代码如下:
IPassenger:
/// <summary> /// 乘客/旅客接口 /// </summary> public interface IPassenger { /// <summary> /// 乘坐交通工具去{targetAddr}(目的地) /// </summary> /// <param name="targetAddr">目的地</param> void ByVehicleTo(string targetAddr); }
Student:
/// <summary> /// 学生旅客 实现了乘客/旅客接口 /// </summary> public class Student : IPassenger { private readonly IVehicle _vehicle; /// <summary> /// 构造方法 /// </summary> /// <param name="vehicle">交通工具</param> public Student(IVehicle vehicle) { _vehicle = vehicle; } /// <summary> /// 乘坐交通工具去{targetAddr}(目的地) /// </summary> /// <param name="targetAddr"></param> public void ByVehicleTo(string targetAddr) { Console.WriteLine("学生旅客:"); _vehicle.GoTo(targetAddr); } }
5、修改Program.cs的代码,如下:
class Program { private static IContainer Container { get; set; } static void Main(string[] args) { var builder = new ContainerBuilder(); builder.RegisterType<Bus>().As<IVehicle>(); //builder.RegisterType<Train>().As<IVehicle>(); builder.RegisterType<Student>().As<IPassenger>(); Container = builder.Build(); Travel(); Console.ReadLine(); } public static void Travel() { using (var scope = Container.BeginLifetimeScope()) { var passenger = scope.Resolve<IPassenger>(); passenger.ByVehicleTo("Beijing"); } } }
直接运行程序,得到结果如下:
代码解读
回顾一下上面的代码,我们定义了两个接口:IPassenger(乘客/旅客接口)和IVehicle(交通工具接口),并且分别定义了Student(学生类)实现了IPassenger接口,以及Bus(巴士类)实现了IVehicle接口。
但是,在Program.cs的代码中,我们并没用任何调用new直接生成Student或Bus对象的代码,下面我们看一下Main方法的代码注释,了解一下IoC容器简单的使用方法:
//以下代码是初始化容器并且注册程序中需要使用到的类型为组件 //实例化一个Autofac.ContainerBuilder对象 var builder = new ContainerBuilder(); //将Bus类型注册为组件,并暴露接口IVehicle作为服务。 builder.RegisterType<Bus>().As<IVehicle>(); //将Student类型注册为组件,并暴露接口IPassenger作为服务。 builder.RegisterType<Student>().As<IPassenger>(); //构建容器 Container = builder.Build();
在看看Travel()方法中的代码:
public static void Travel(){ //创建了一个生命周期范围(scope), 从中可以解析依赖项 using (var scope = Container.BeginLifetimeScope()) { //从生命周期范围(scope)中解析IPassenger, //Autofac发现IPassenger对应Student,因此开始创建Student //Autofac发现Student的构造方法需要一个IVehicle(这就是“构造方法注入”) //Autofac发现IVehicle对应Bus,因此开始创建新的Bus实例 //Autofac使用新的Bus实例完成Student的创建 //Autofac返回完整构建的 Student 给"Travel"方法使用 var passenger = scope.Resolve<IPassenger>(); //调用 passenger.ByVehicleTo("Beijing") 就是一个全新的 passenger.ByVehicleTo("Beijing") 因为这是在生命周期中解析出的 passenger.ByVehicleTo("Beijing"); //Autofac生命周期被释放. 任何从生命周期解析出的可释放对象也都被同时释放。 //例如这里的Bus实现了IDisposable接口,在scope生命周期被释放的同时会调用Bus的Dispose。 } }
如果后面需要增加一个交通工具,例如:Train(火车),同样实现接口IVehicle,代码如下:
public class Train : IVehicle { public void GoTo(string targetAddress) { Console.WriteLine($"乘坐火车去 {targetAddress}"); } }
然后只需修改Program.cs中Main方法内的IoC容器注册组件部分代码,如下:
static void Main(string[] args) { var builder = new ContainerBuilder(); //builder.RegisterType<Bus>().As<IVehicle>(); //这行代码注释掉 builder.RegisterType<Train>().As<IVehicle>(); //加上这行代码 builder.RegisterType<Student>().As<IPassenger>(); Container = builder.Build(); Travel(); Console.ReadLine(); }
然后运行程序,得出以下结果:
Travel()方法里面的代码我们一点都没改,只是将IVehicle服务的对应组件注册类型修改为Train,结果就变成使用Train类的实例化运行了。这个就是IoC“控制反转”,也叫DI“依赖注入”。
参考:https://autofaccn.readthedocs.io/zh/latest/getting-started/index.html