关于.NET中的控制反转(三)- 依赖注入之 Autofac
一、Autofac简介
Autofac和其他容器的不同之处是它和C#语言的结合非常紧密,在使用过程中对你的应用的侵入性几乎为零,更容易与第三方的组件集成。Autofac的主要特性如下:
- 组件侵入性为零:组件不需要去引用Autofac。
- 灵活的模块化系统:通过模块化组织你的程序,应用程序不用纠缠于复 杂的XML配置系统或者是配置参数。
- 自动装配:可以是用lambda表达式注册你的组件,autofac会根据需要选择构造函数或者属 性注入
- XML配置文件的支持:XML配置文件过度使用时很丑陋,但是在发布的时候通常非常有用
- 组件的多服务支持:许 多设计师喜欢使用细粒度的接口来控制依赖 , autofac允许一个组件提供多个服务
二、Autofac安装
1:使用Nuget下载 “Autofac”库,需注意,使用 .net framework必须在4.7以上版本,低于4.7版本的安装会失败。
三、Autofac的注册 —— 使用类型进行注册与使用
1 using Autofac; 2 using System; 3 4 namespace BankOperation 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 // 创建 ContainerBuilder 11 ContainerBuilder builder = new ContainerBuilder(); 12 13 // 注册 14 builder.RegisterType<ABank>().As<IUnionPay>(); 15 16 // 实例化 17 IContainer container = builder.Build(); 18 var dev = container.Resolve<IUnionPay>(); 19 dev.SaveMoneny(80); 20 dev.WithdrawMoney(50); 21 22 Console.ReadKey(); 23 } 24 25 } 26 27 public interface IUnionPay 28 { 29 /// <summary> 30 /// 存钱 31 /// </summary> 32 /// <param name="amount">存钱金额</param> 33 void SaveMoneny(int amount); 34 35 /// <summary> 36 /// 取钱 37 /// </summary> 38 /// <param name="amount">取钱金额</param> 39 void WithdrawMoney(int amount); 40 } 41 42 public class ABank:IUnionPay 43 { 44 public void SaveMoneny(int amount) 45 { 46 Console.WriteLine($"把钱存入A银行,金额为:{amount}"); 47 } 48 49 public void WithdrawMoney(int amount) 50 { 51 Console.WriteLine($"从A银行取钱,金额为:{amount}"); 52 } 53 } 54 }
四、Autofac的注册 —— 使用别名进行注册与使用(一个接口有多个实现类时)
1 using Autofac; 2 using System; 3 4 namespace BankOperation 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 Console.Write("请输入银行名称:"); 11 string name = Console.ReadLine(); 12 Operation(name); 13 14 Console.ReadKey(); 15 } 16 17 static void Operation(string name) 18 { 19 // 创建 ContainerBuilder 20 ContainerBuilder builder = new ContainerBuilder(); 21 22 // 使用别名进行注册 23 builder.RegisterType<ABank>().Named<IUnionPay>("A"); 24 builder.RegisterType<BBank>().Named<IUnionPay>("B"); 25 26 // 使用别名进行实例化 27 IContainer container = builder.Build(); 28 var dev = container.ResolveNamed<IUnionPay>(name); 29 dev.SaveMoneny(80); 30 dev.WithdrawMoney(50); 31 } 32 } 33 34 public interface IUnionPay 35 { 36 /// <summary> 37 /// 存钱 38 /// </summary> 39 /// <param name="amount">存钱金额</param> 40 void SaveMoneny(int amount); 41 42 /// <summary> 43 /// 取钱 44 /// </summary> 45 /// <param name="amount">取钱金额</param> 46 void WithdrawMoney(int amount); 47 } 48 49 public class ABank:IUnionPay 50 { 51 public void SaveMoneny(int amount) 52 { 53 Console.WriteLine($"把钱存入A银行,金额为:{amount}"); 54 } 55 56 public void WithdrawMoney(int amount) 57 { 58 Console.WriteLine($"从A银行取钱,金额为:{amount}"); 59 } 60 } 61 62 public class BBank : IUnionPay 63 { 64 public void SaveMoneny(int amount) 65 { 66 Console.WriteLine($"把钱存入B银行,金额为:{amount}"); 67 } 68 69 public void WithdrawMoney(int amount) 70 { 71 Console.WriteLine($"从B银行取钱,金额为:{amount}"); 72 } 73 } 74 }
五、Autofac的注册 —— 使用Lambda进行注册与使用(传递参数时)
反射在组件创建时是个很好的选择. 但是, 当组件创建不再是简单的调用构造方法时, 事情将变得混乱起来。可通过Lambda表达式传递一个实例,通过实例的初始化函数和公共属性传递需要注入的信息:
1 using Autofac; 2 using System; 3 4 namespace BankOperation 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 // 创建 ContainerBuilder 11 ContainerBuilder builder = new ContainerBuilder(); 12 13 // 注册(通过实例化函数传递参数+类的公共属性) 14 builder.Register<IUnionPay>(c => new ABank("测试A银行"){ BankRate =8.12}); 15 16 // 实例化 17 IContainer container = builder.Build(); 18 var dev = container.Resolve<IUnionPay>(); 19 dev.SaveMoneny(80); 20 dev.WithdrawMoney(50); 21 22 Console.ReadKey(); 23 } 24 25 } 26 27 public interface IUnionPay 28 { 29 /// <summary> 30 /// 存钱 31 /// </summary> 32 /// <param name="amount">存钱金额</param> 33 void SaveMoneny(int amount); 34 35 /// <summary> 36 /// 取钱 37 /// </summary> 38 /// <param name="amount">取钱金额</param> 39 void WithdrawMoney(int amount); 40 } 41 42 public class ABank:IUnionPay 43 { 44 /// <summary> 45 /// 银行利率 46 /// </summary> 47 public double BankRate { get; set; } 48 public ABank(string name) 49 { 50 Console.WriteLine($"银行名称:{name}"); 51 } 52 public void SaveMoneny(int amount) 53 { 54 Console.WriteLine($"把钱存入A银行(利率:{BankRate}%),金额为:{amount}"); 55 } 56 57 public void WithdrawMoney(int amount) 58 { 59 Console.WriteLine($"从A银行取钱,金额为:{amount}"); 60 } 61 } 62 }
当接口类有多个实现类,实现方式如下:
1 using Autofac; 2 using System; 3 4 namespace BankOperation 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 Console.Write("请输入银行名称:"); 11 string name = Console.ReadLine(); 12 Operation(name); 13 14 Console.ReadKey(); 15 } 16 17 static void Operation(string name) 18 { 19 // 创建 ContainerBuilder 20 ContainerBuilder builder = new ContainerBuilder(); 21 22 // 注册(通过实例化函数传递参数+类的公共属性) 23 builder.Register(c => new ABank("测试A银行") { BankRate = 8.12 }).Named<IUnionPay>("A"); 24 builder.Register(c => new ABank("测试B银行") { BankRate = 10.25 }).Named<IUnionPay>("B"); 25 26 // 实例化 27 IContainer container = builder.Build(); 28 var dev = container.ResolveNamed<IUnionPay>(name); 29 dev.SaveMoneny(80); 30 dev.WithdrawMoney(50); 31 } 32 33 } 34 35 public interface IUnionPay 36 { 37 /// <summary> 38 /// 存钱 39 /// </summary> 40 /// <param name="amount">存钱金额</param> 41 void SaveMoneny(int amount); 42 43 /// <summary> 44 /// 取钱 45 /// </summary> 46 /// <param name="amount">取钱金额</param> 47 void WithdrawMoney(int amount); 48 } 49 50 public class ABank:IUnionPay 51 { 52 /// <summary> 53 /// 银行利率 54 /// </summary> 55 public double BankRate { get; set; } 56 public ABank(string name) 57 { 58 Console.WriteLine($"银行名称:{name}"); 59 } 60 public void SaveMoneny(int amount) 61 { 62 Console.WriteLine($"把钱存入A银行(利率:{BankRate}%),金额为:{amount}"); 63 } 64 65 public void WithdrawMoney(int amount) 66 { 67 Console.WriteLine($"从A银行取钱,金额为:{amount}"); 68 } 69 } 70 71 public class BBank : IUnionPay 72 { 73 /// <summary> 74 /// 银行利率 75 /// </summary> 76 public double BankRate { get; set; } 77 public BBank(string name) 78 { 79 Console.WriteLine($"银行名称:{name}"); 80 } 81 public void SaveMoneny(int amount) 82 { 83 Console.WriteLine($"把钱存入B银行(利率:{BankRate}%),金额为:{amount}"); 84 } 85 86 public void WithdrawMoney(int amount) 87 { 88 Console.WriteLine($"从B银行取钱,金额为:{amount}"); 89 } 90 } 91 }
四、程序集注册
以上方法全部都要进行手动注册,如果有很多接口及实现类,这种一一注册的方式很麻烦,我们可以一次性全部注册,当然也可以加筛选条件。
1 using Autofac; 2 using System; 3 using System.Collections.Generic; 4 using System.Reflection; 5 6 namespace BankOperation 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 Operation(); 13 14 Console.ReadKey(); 15 } 16 17 static void Operation() 18 { 19 // 创建 ContainerBuilder 20 ContainerBuilder builder = new ContainerBuilder(); 21 22 //实现类所在的程序集名称 23 Assembly assembly = Assembly.Load("BankOperation"); 24 25 // 获取程序集所在的全部实现类 26 builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces(); 27 28 // t.Name.StartsWith:通过类名添加筛选条件,从类名的起始位开始匹配 29 builder.RegisterAssemblyTypes(assembly).Where(t => t.Name.StartsWith("B")).AsImplementedInterfaces(); 30 31 // 实例化 32 IContainer container = builder.Build(); 33 IEnumerable<IUnionPay> banks = container.Resolve<IEnumerable<IUnionPay>>(); 34 35 foreach (var item in banks) 36 { 37 item.SaveMoneny(100); 38 item.WithdrawMoney(20); 39 Console.WriteLine("-----------------------------------------------"); 40 } 41 } 42 43 } 44 45 public interface IUnionPay 46 { 47 /// <summary> 48 /// 存钱 49 /// </summary> 50 /// <param name="amount">存钱金额</param> 51 void SaveMoneny(int amount); 52 53 /// <summary> 54 /// 取钱 55 /// </summary> 56 /// <param name="amount">取钱金额</param> 57 void WithdrawMoney(int amount); 58 } 59 60 public class ABank:IUnionPay 61 { 62 public void SaveMoneny(int amount) 63 { 64 Console.WriteLine($"把钱存入A银行,金额为:{amount}"); 65 } 66 67 public void WithdrawMoney(int amount) 68 { 69 Console.WriteLine($"从A银行取钱,金额为:{amount}"); 70 } 71 } 72 73 public class BBank : IUnionPay 74 { 75 public void SaveMoneny(int amount) 76 { 77 Console.WriteLine($"把钱存入B银行,金额为:{amount}"); 78 } 79 80 public void WithdrawMoney(int amount) 81 { 82 Console.WriteLine($"从B银行取钱,金额为:{amount}"); 83 } 84 } 85 }
五、引用地址
1:Autofac官网地址:https://autofaccn.readthedocs.io/zh/latest/
2:Autofac 测试用例代码:https://github.com/autofac/Autofac