一. 說在前面的話html
本節的內容主要包括:編程
1. 在使用IOC框架以前的幾種建立對象的方式。mvc
2. AutoFac的基本用法和幾種生命週期。app
3. AutoFac和Asp.Net MVC5進行整合,利用屬性的方式進行注入。框架
事先說明一下本節要用到的實現類和接口類:ide
(1). Ypf.BLL層中包括:CatBLL、DogBLL、RoleBLL、UserBLL。函數
1 public class CatBLL : IAnimalBLL 2 { 3 public string Introduce() 4 { 5 return "我是貓"; 6 } 7 }
1 public class DogBLL : IAnimalBLL 2 { 3 public string Introduce() 4 { 5 return "我是狗"; 6 } 7 }
1 public class RoleBLL : IRoleBLL 2 { 3 4 public IUserBLL userBLL { get; set; } 5 6 /// <summary> 7 /// 展現角色信息 8 /// </summary> 9 /// <returns></returns> 10 public string ShowRoleInfor() 11 { 12 return "我是管理員角色"; 13 } 14 15 16 public string ShowDIDemo() 17 { 18 return "哈哈:" + userBLL.GetUserInfor(); 19 } 20 21 }
1 public class UserBLL : IUserBLL,IPeopleBLL 2 { 3 /// <summary> 4 /// 獲取用戶信息 5 /// </summary> 6 /// <returns></returns> 7 public string GetUserInfor() 8 { 9 return "我是獲取用戶信息的方法"; 10 } 11 12 /// <summary> 13 /// 自我介紹 14 /// </summary> 15 /// <returns></returns> 16 public string Introduce() 17 { 18 return "我是ypf"; 19 } 20 }
(2). Ypf.IBLL層包括:IAnimalBLL、IPeopleBLL、IRoleBLL、IUserBLL。測試
1 public interface IAnimalBLL 2 { 3 string Introduce(); 4 }
1 public interface IPeopleBLL 2 { 3 //自我介紹 4 string Introduce(); 5 }
1 public interface IRoleBLL 2 { 3 string ShowRoleInfor(); 4 5 string ShowDIDemo(); 6 7 }
1 public interface IUserBLL 2 { 3 string GetUserInfor(); 4 }
二. 引入IOC框架以前的幾個寫法ui
1. 最原始的方式直接new(需添加對BLL層的引用)spa
1 { 2 UserBLL userBll = new UserBLL(); 3 var result1 = userBll.GetUserInfor(); 4 Console.WriteLine(result1); 5 }
2. 面向接口編程(仍需添加對BLL層的引用)
1 { 2 IUserBLL userBll = new UserBLL(); 3 var result1 = userBll.GetUserInfor(); 4 Console.WriteLine(result1); 5 }
3. 接口+反射(只需將BLL層的程序集拷貝進來)
1 { 2 Assembly ass = Assembly.Load("Ypf.BLL"); 3 Type type = ass.GetType("Ypf.BLL.UserBLL"); 4 //調用默認的無參構造函數進行對象的建立 5 object myUserBLL = Activator.CreateInstance(type); 6 IUserBLL userBLL = (IUserBLL)myUserBLL; 7 var result1 = userBLL.GetUserInfor(); 8 Console.WriteLine(result1); 9 10 }
4. 手寫IOC(反射+簡單工廠+配置文件)【需將BLL層的程序集拷貝進來】
配置文件代碼:
<appSettings> <!--直接修改配置文件,能夠切換IUserBLL的實現類,發佈後能夠直接經過改配置文件,代碼什麼也不用改,體會:反射+面向接口編程--> <add key="DllName" value="Ypf.BLL"/> <add key="ClassName" value="Ypf.BLL.UserBLL"/> </appSettings>
簡單工廠代碼:
1 /// <summary> 2 /// 簡單工廠,隔離對象的建立 3 /// </summary> 4 public class SimpleFactory 5 { 6 private static string DllName = ConfigurationManager.AppSettings["DllName"]; 7 private static string ClassName = ConfigurationManager.AppSettings["ClassName"]; 8 public static IUserBLL CreateInstance() 9 { 10 Assembly ass = Assembly.Load(DllName); 11 Type type = ass.GetType(ClassName); 12 object obj = Activator.CreateInstance(type); 13 return (IUserBLL)obj; 14 } 15 }
調用代碼:
1 { 2 IUserBLL userBLL = SimpleFactory.CreateInstance(); 3 var result = userBLL.GetUserInfor(); 4 Console.WriteLine(result); 5 }
三. AutoFac常見用法總結
1. 基本用法
同時添加對Ypf.BLL層和Ypf.IBLL層的引用,而後 聲明容器→註冊實例→解析對象→調用方法、進行測試,代碼以下:
1 { 2 ContainerBuilder builder = new ContainerBuilder(); 3 //把UserBLL註冊爲IUserBLL實現類,當請求IUserBLL接口的時候,返回UserBLL對象 4 builder.RegisterType<UserBLL>().As<IUserBLL>(); 5 IContainer resolver = builder.Build(); 6 IUserBLL userBLL = resolver.Resolve<IUserBLL>(); 7 var result1 = userBLL.GetUserInfor(); 8 Console.WriteLine(result1); 9 }
評價:這種用法單純的是爲了介紹AutoFac中的幾個方法,僅此而已,在實際開發沒有這麼用的,坑比用法,起不到任何解耦的做用。
2. AsImplementedInterfaces的用法
在不少狀況下,一個類可能實現了多個接口, 若是咱們經過 builder.RegisterType<xxxBLL>().As<IxxxBLL>(); 這種方式循序漸進排着把這個類註冊給每一個接口,實現幾個接口,就要寫幾行註冊代碼,很繁瑣,咱們能夠經過 AsImplementedInterfaces() 方法,能夠把一個類註冊給它實現的所有接口。
這樣的話,想用哪一個接口,經過Resolve解析便可,代碼以下:
1 { 2 ContainerBuilder builder = new ContainerBuilder(); 3 //這樣請求UserBLL實現的任何接口的時候都會返回 UserBLL 對象。 4 builder.RegisterType<UserBLL>().AsImplementedInterfaces(); 5 IContainer resolver = builder.Build(); 6 IUserBLL iUserBLL = resolver.Resolve<IUserBLL>(); 7 IPeopleBLL iPeopleBLL = resolver.Resolve<IPeopleBLL>(); 8 9 var r1 = iUserBLL.GetUserInfor(); 10 var r2 = iPeopleBLL.Introduce(); 11 12 Console.WriteLine(r1); 13 Console.WriteLine(r2); 14 }
評價:同時添加對Ypf.BLL層和Ypf.IBLL層的引用,這裏也是單純的爲了介紹AsImplementedInterfaces()的用法,仍是存在實現類的身影,在實際開發中沒有這麼用的,起不到任何解耦的做用,坑比用法。
3. AutoFac+反射(完全消滅實現類)
引入反射的背景:前面兩種方式都須要添加對Ypf.BLL層的引用,麻煩的要死,根本沒有什麼改觀,仍是緊耦合在一塊兒。而且若是有不少接口和實現類的話,用RegisterType一行一行的去寫,累個半死,在這種狀況下引入反射的概念,簡化代碼量,代碼以下:
1 { 2 ContainerBuilder builder = new ContainerBuilder(); 3 //加載實現類的程序集 4 Assembly asm = Assembly.Load("Ypf.BLL"); 5 builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces(); 6 IContainer resolver = builder.Build(); 7 8 IUserBLL userBLL = resolver.Resolve<IUserBLL>(); 9 IPeopleBLL peopleBLL = resolver.Resolve<IPeopleBLL>(); 10 var r1 = userBLL.GetUserInfor(); 11 var r2 = peopleBLL.Introduce(); 12 13 Console.WriteLine(r1); 14 Console.WriteLine(r2); 15 }
評價:完全擺脫了實現類的身影,與Ypf.BLL層進行了解耦,只須要添加對Ypf.IBLL層的引用,但須要把Ypf.BLL的程序集拷貝到AutoFacTest項目下。
小小的升級一下:
把反射那個程序集類寫到配置文件中,而後在代碼中經過讀取配置文件進行進一步的反射,代碼以下:
1 <appSettings> 2 <add key="DllName" value="Ypf.BLL"/> 3 </appSettings>
1 { 2 ContainerBuilder builder = new ContainerBuilder(); 3 //加載實現類的程序集 4 string DllName = ConfigurationManager.AppSettings["DllName"]; 5 Assembly asm = Assembly.Load(DllName); 6 builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces(); 7 IContainer resolver = builder.Build(); 8 9 IUserBLL userBLL = resolver.Resolve<IUserBLL>(); 10 IPeopleBLL peopleBLL = resolver.Resolve<IPeopleBLL>(); 11 var r1 = userBLL.GetUserInfor(); 12 var r2 = peopleBLL.Introduce(); 13 14 Console.WriteLine(r1); 15 Console.WriteLine(r2); 16 }
4. PropertiesAutowired(屬性的自動注入)
背景:一個實現類中定義了其餘類型的接口屬性,好比RoleBLL中定義IUserBLL的接口屬性,並且要對其進行調用, 這個時候就須要經過PropertiesAutowired實現屬性的自動注入了。
注:只有經過AutoFac建立的對象才能實現屬性的自動注入!! 相關的類、接口要是public類型。
1 public class RoleBLL : IRoleBLL 2 { 3 4 public IUserBLL userBLL { get; set; } 5 6 /// <summary> 7 /// 展現角色信息 8 /// </summary> 9 /// <returns></returns> 10 public string ShowRoleInfor() 11 { 12 return "我是管理員角色"; 13 } 14 15 16 public string ShowDIDemo() 17 { 18 return "哈哈:" + userBLL.GetUserInfor(); 19 } 20 21 22 23 }
1 { 2 ContainerBuilder builder = new ContainerBuilder(); 3 //加載實現類的程序集 4 Assembly asm = Assembly.Load("Ypf.BLL"); 5 builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces().PropertiesAutowired(); 6 IContainer resolver = builder.Build(); 7 8 IRoleBLL iRoleBLL = resolver.Resolve<IRoleBLL>(); 9 var r1 = iRoleBLL.ShowDIDemo(); 10 Console.WriteLine(r1); }
下面測試一下不是AutoFac建立的對象可否實現屬性的自動注入,新建TempTest類,在裏面聲明IUserBLL屬性,而且在方法中進行調用,而後new一個TempTest對象,對該showMsg方法進行調用,發現報空指針錯誤,說明userBLL屬性爲空,沒能自動注入。
1 public class TempTest 2 { 3 public IUserBLL userBLL { get; set; } 4 5 public void showMsg() 6 { 7 Console.WriteLine(userBLL.GetUserInfor()); 8 } 9 }
1 //測試本身new的對象不能實現屬性的自動注入 2 //下面代碼報空指針錯誤 3 { 4 TempTest t = new TempTest(); 5 t.showMsg(); 6 }
5. 1個接口多個實現類的狀況
背景:1個接口有多個實現類的狀況(DogBLL 和 CatBLL 都實現了 IAnimalBLL接口)
分析:resolver.Resolve<IAnimalBLL>();只會返回其中一個類的對象
解決方案:若是想返回多個實現類的對象,改爲 resolver.Resolve<IEnumerable<IAnimalBLL>>()便可。
1 { 2 ContainerBuilder builder = new ContainerBuilder(); 3 //加載實現類的程序集 4 Assembly asm = Assembly.Load("Ypf.BLL"); 5 builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces().PropertiesAutowired(); 6 IContainer resolver = builder.Build(); 7 8 //返回 CalBLL 和 DogBLL 中的一個 9 //{ 10 // IAnimalBLL iAnimalBLL = resolver.Resolve<IAnimalBLL>(); 11 // var r1 = iAnimalBLL.Introduce(); 12 // Console.WriteLine(r1); 13 //} 14 15 //如何獲取多個呢? 16 { 17 IEnumerable<IAnimalBLL> blls = resolver.Resolve<IEnumerable<IAnimalBLL>>(); 18 foreach (IAnimalBLL animalBLL in blls) 19 { 20 Console.WriteLine(animalBLL.GetType()); 21 Console.WriteLine(animalBLL.Introduce()); 22 } 23 } 24 }
6. AutoFac的幾種常見生命週期
1. InstancePerDependency:每次請求 Resovle都返回一個新對象。InstancePerDependency()【這也是默認的建立實例的方式。】
2. SingleInstance: 單例,只有在第一次請求的時候建立 。SingleInstance()
3. InstancePerRequest:ASP.Net MVC 專用,每次http請求內一個對象(也能夠理解爲一個方法內)。InstancePerRequest() 和 CallContext神似
4. InstancePerLifetimeScope:在一個生命週期域中,每個依賴或調用建立一個單一的共享的實例,且每個不一樣的生命週期域,實例是惟一的,不共享的。
下面測試一下前兩種生命週期
狀況1
1 { 2 ContainerBuilder builder = new ContainerBuilder(); 3 //加載實現類的程序集 4 Assembly asm = Assembly.Load("Ypf.BLL"); 5 builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces().PropertiesAutowired().InstancePerDependency(); 6 IContainer resolver = builder.Build(); 7 8 IUserBLL u1 = resolver.Resolve<IUserBLL>(); 9 IUserBLL u2 = resolver.Resolve<IUserBLL>(); 10 11 Console.WriteLine(object.ReferenceEquals(u1, u2)); 12 13 }
結果:False,證實InstancePerDependency 每次都建立一個新對象
狀況2
1 { 2 ContainerBuilder builder = new ContainerBuilder(); 3 //加載實現類的程序集 4 Assembly asm = Assembly.Load("Ypf.BLL"); 5 builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces().PropertiesAutowired().SingleInstance(); 6 IContainer resolver = builder.Build(); 7 8 IUserBLL u1 = resolver.Resolve<IUserBLL>(); 9 IUserBLL u2 = resolver.Resolve<IUserBLL>(); 10 11 Console.WriteLine(object.ReferenceEquals(u1, u2)); 12 13 }
結果:true,證實SingleInstance 每次都返回同一個對象。
四. AutoFac與MVC整合
1. Controller中經過屬性注入對象
步驟1:在Ypf.MVC層中添加對Ypf.IBLL層的引用,並將Ypf.BLL的程序集拷貝到 Ypf.MVC中,或者直接改一下Ypf.BLL輸出路徑。
步驟2:經過Nuget安裝程序集 Autofac.Mvc5。
步驟3:在Gloabl 註冊 AutoFac代碼。
1 public class MvcApplication : System.Web.HttpApplication 2 { 3 protected void Application_Start() 4 { 5 AreaRegistration.RegisterAllAreas(); 6 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 7 RouteConfig.RegisterRoutes(RouteTable.Routes); 8 BundleConfig.RegisterBundles(BundleTable.Bundles); 9 10 /***********下面是AutoFac的註冊*************/ 11 //1. 建立容器 12 var builder = new ContainerBuilder(); 13 //2. 把當前程序集中的全部Controller都註冊進來 14 builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired(); 15 //3. 把Ypf.BLL中的全部類註冊給它的所有實現接口,而且把實現類中的屬性也進行註冊 16 //{ Assembly asmService = Assembly.Load("Ypf.BLL"); } 17 //PS:這裏能夠配合配置文件的,將Ypf.BLL寫到配置文件中 18 string DllName = ConfigurationManager.AppSettings["DllName"]; 19 Assembly asmService = Assembly.Load(DllName); 20 builder.RegisterAssemblyTypes(asmService).Where(t => !t.IsAbstract).AsImplementedInterfaces().PropertiesAutowired(); 21 var container = builder.Build(); 22 //4. 下面這句話表示當mvc建立controller對象的時候,都是由AutoFac爲咱們建立Controller對象 23 DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); 24 25 26 } 27 }
PS:分享個小技巧
步驟4:在Controller中進行調用。
2. 普通類中經過代碼獲取對象
在一個沒有經過AutoFac註冊的普通類中如何獲取接口對象呢,經過DependencyResolver.Current.GetService<IUserBLL>();來獲取。
代碼以下:
1 public class Utils 2 { 3 public static string Test() 4 { 5 IUserBLL userBLL = DependencyResolver.Current.GetService<IUserBLL>(); 6 return userBLL.GetUserInfor(); 7 } 8 }
3. 如何在普通類中經過屬性的方式注入對象
須要有兩個條件:
①: 這個普通類的建立必須在Global中經過AutoFac來進行註冊。
②: 獲取這個類的時候必須經過 DependencyResolver.Current.GetService<IUserBLL>(); 這種方式來獲取。
在Global文件中註冊該普通類
該普通類CommonHelp的獲取必須經過DependencyResolver.Current.GetService<CommonHelp>();方式來獲取。
4. 在單獨線程中獲取對象
好比在Quartz.Net 中,須要經過下面代碼來獲取。
詳細代碼以下:
{ //1.建立做業調度池(Scheduler) IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler(); //2.建立一個具體的做業即job (具體的job須要單獨在一個文件中執行) var job = JobBuilder.Create<HelloJob>().Build(); //3.建立並配置一個觸發器即trigger 1s執行一次 var trigger = TriggerBuilder.Create().WithSimpleSchedule(x => x.WithIntervalInSeconds(1) .RepeatForever()).Build(); //4.將job和trigger加入到做業調度池中 scheduler.ScheduleJob(job, trigger); //5.開啓調度 scheduler.Start(); }
1 public class HelloJob:IJob 2 { 3 void IJob.Execute(IJobExecutionContext context) 4 { 5 IUserBLL userBLL; 6 var container = AutofacDependencyResolver.Current.ApplicationContainer; 7 using (container.BeginLifetimeScope()) 8 { 9 userBLL = container.Resolve<IUserBLL>(); 10 } 11 //下面代碼只是測試 12 Console.WriteLine(userBLL.GetUserInfor()); 13 } 14 }
!