前言 html
以前的前5篇做爲EF方面的基礎篇,後面咱們將使用MVC+EF 而且使用IOC ,Repository,UnitOfWork,DbContext來總體來學習。由於後面要用到IOC,因此本篇先單獨先學習一下IOC,咱們本本文單獨主要學習Autofac,其實對於Autofac我也是邊學邊記錄。不對的地方,也但願你們多多指導。git
我的在學習過程當中參考博客:github
AutoFac文檔:http://www.cnblogs.com/wolegequ/archive/2012/06/09/2543487.htmlweb
AutoFac使用方法總結:Part I:http://niuyi.github.io/blog/2012/04/06/autofac-by-unit-test/數據庫
爲何使用AutoFac?編程
Autofac是.NET領域最爲流行的IOC框架之一,傳說是速度最快的一個:框架
優勢:ide
- 它是C#語言聯繫很緊密,也就是說C#裏的不少編程方式均可覺得Autofac使用,例如能夠用Lambda表達式註冊組件
- 較低的學習曲線,學習它很是的簡單,只要你理解了IoC和DI的概念以及在什麼時候須要使用它們
- XML配置支持
- 自動裝配
- 與Asp.Net MVC 集成
- 微軟的Orchad開源程序使用的就是Autofac,從該源碼能夠看出它的方便和強大
上面的優勢我也是拷的別人文章裏面的,上面的這個幾乎全部講Autofac博文都會出現的。這個也是首次學習,因此咱們仍是記錄的細一點。函數
怎麼使用Autofacpost
經過VS中的NuGet來加載AutoFac,引入成功後引用就會出現Autofac。
一、咱們作一個簡單的例子先用一下
就拿數據訪問來作案例把,一個數據請求有兩個類,一個是Oracle 一個是SQLSERVER。咱們在使用的時候能夠選擇調用那個數據庫。
1.1 咱們先定義一個數據訪問的接口和訪問類。
/// <summary> /// 數據源操做接口 /// </summary> public interface IDataSource { /// <summary> /// 獲取數據 /// </summary> /// <returns></returns> string GetData(); }
/// <summary> /// SQLSERVER數據庫 /// </summary> class Sqlserver : IDataSource { public string GetData() { return "經過SQLSERVER獲取數據"; } }
/// <summary> /// ORACLE數據庫 /// </summary> public class Oracle : IDataSource { public string GetData() { return "經過Oracle獲取數據"; } }
最普通的方式你們都會的吧! 若是最普通的方式調用SQLSERVER怎麼寫?
static void Main(string[] args) { IDataSource ds = new Sqlserver(); Console.WriteLine(ds.GetData()); Console.ReadLine(); }調用Oracle的話new Oracle()就能夠了。若是這個都不能理解的話,那學習這個你就很費勁了。
改進一下代碼。咱們在加入一個DataSourceManager類來看一下
/// <summary>/// 數據源管理類/// </summarypublic class DataSourceManager { IDataSource _ds; /// <summary> /// 根據傳入的類型動態建立對象 /// </summary> /// <param name="ds"></param> public DataSourceManager(IDataSource ds) { _ds = ds; } public string GetData() { return _ds.GetData(); } }這樣寫的好處是什麼,這樣加入加入新的數據源,只用調用的時候傳入這個對象就能夠,就會自動建立一個對應的對象。那接下若是要調用SQLSERVER怎麼寫。看代碼
DataSourceManager dsm = new DataSourceManager(new Sqlserver());Console.WriteLine(dsm.GetData()); Console.ReadLine();
1.2 注入實現構造函數注入
上面的DataSourceManager的動態建立的方式就是由於又有個帶IDataSource的參數的構造函數,只要調用者傳入實現該接口的對象,就實現了對象建立。
那咱們看看怎麼使用AutoFac注入實現構造函數注入
var builder = new ContainerBuilder(); builder.RegisterType<DataSourceManager>(); builder.RegisterType<Sqlserver>().As<IDataSource>(); using (var container = builder.Build()) { var manager = container.Resolve<DataSourceManager>(); Console.WriteLine(manager.GetData()); Console.ReadLine(); }
上面的就是AutoFac構造函數注入,他給IDataSource注入的是Sqlserver因此咱們調用的數據,返回的就是Sqlserver數據。那下面咱們具體的瞭解一下AutoFac的一些方法
1.3 Autofac方法說明
(1)builder.RegisterType<Object>().As<Iobject>():註冊類型及其實例。例如上面就是註冊接口IDataSource的實例Sqlserver
(2)IContainer.Resolve<IDAL>():解析某個接口的實例。例如一下代碼,我能夠解析接口返回的就是Sqlserver實例
var builder = new ContainerBuilder();
//builder.RegisterType<DataSourceManager>();
builder.RegisterType<Sqlserver>().As<IDataSource>();using (var container = builder.Build())
{
var manager = container.Resolve<IDataSource>();
Console.WriteLine(manager.GetData());Console.ReadLine();
}(3)builder.RegisterType<Object>().Named<Iobject>(string name):爲一個接口註冊不一樣的實例。有時候不免會碰到多個類映射同一個接口,好比Sqlerver和Oracle都實現了IDalSource接口,爲了準確獲取想要的類型,就必須在註冊時起名字。
運行後的代碼。var builder = new ContainerBuilder(); builder.RegisterType<Sqlserver>().Named<IDataSource>("SqlServer"); builder.RegisterType<Oracle>().Named<IDataSource>("Oracel"); using (var container = builder.Build()) { var manager = container.ResolveNamed<IDataSource>("Oracel"); Console.WriteLine(manager.GetData()); Console.ReadLine(); }(4)IContainer.ResolveNamed<IDAL>(string name):解析某個接口的「命名實例」。例如上面的實例最後一行代碼container.ResolveNamed<IDataSource>("Oracel" ); 就是解析IDataSource的命名實例 Oracel。(5)builder.RegisterType<Object>().Keyed<Iobject>(Enum enum):以枚舉的方式爲一個接口註冊不一樣的實例。有時候咱們會將某一個接口的不一樣實現用枚舉來區分,而不是字符串。這個方法是徹底能夠替代builder.RegisterType<Object>().Named<Iobject>(string name),這個列子就不演示了吧!和上面的一個意思。
(6)IContainer.ResolveKeyed<IDAL>(Enum enum):根據枚舉值解析某個接口的特定實例。這個和上面的都同樣 也就不演示了。(7)builder.RegisterType<Worker>().InstancePerDependency():用於控制對象的生命週期,每次加載實例時都是新建一個實例,默認就是這種方式。調用的話
builder.RegisterType<Sqlserver>().Keyed<IDataSource>("Sqlserver").InstancePerDependency();(8)builder.RegisterType<Worker>().SingleInstance():用於控制對象的生命週期,每次加載實例時都是返回同一個實例
(9)IContainer.Resolve<T>(NamedParameter namedParameter):在解析實例T時給其賦值,這個就是給你定義的方法的參數傳值。
我把DataSourceManager的構造方法加了個name參數,而後我調用的時候:IDataSource _ds; string Name; /// <summary> /// 根據傳入的類型動態建立對象 /// </summary> /// <param name="ds"></param> public DataSourceManager(string name, IDataSource ds) { _ds = ds; Name = name; } public string GetData() { returnName + ":" + _ds.GetData(); }var manager = container.Resolve<DataSourceManager>(new NamedParameter("name", "STONE劉先生"));運行後的代碼:
1.4 經過配置的方式使用AutoFac
在演示一下怎麼經過配置文件來配置註冊。這塊就簡單講,下面的是個人web.config。
<configuration> <configSections> <section name="autofac" type="Autofac.Configuration.SectionHandler,Autofac.Configuration"></section> </configSections> <autofac defaultAssembly="AutoFacDemo"> <components> <component type="AutoFacDemo.Model.Oracle,AutoFacDemo" service="AutoFacDemo.Model.IDataSource" /> </components> </autofac> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> </configuration>
後臺的調用代碼
var builder = new ContainerBuilder(); builder.RegisterType<DataSourceManager>(); builder.RegisterModule(new ConfigurationSettingsReader("autofac")); using (var container = builder.Build()) { var manager = container.Resolve<DataSourceManager>(new NamedParameter("name", "STONE劉先生")); Console.WriteLine(manager.GetData()); Console.ReadLine(); }這裏須要注意的是須要引用Autofac.Configuration.dll,不然沒有辦法使用ConfigurationSettingsReader。
還有一個須要注意的就是你的配置文件要命名空間,類名要寫對。
動手嘗試一下吧!
引用和上面的控制檯程序的原理是如出一轍的。可是區別就在於要多添加一個引用
案例仍是用上面的案例。我是把以前的接口和類拷貝到MVC項目裏面做爲下面演示。代碼就不在寫出來了,如出一轍的。
MVC下怎麼配置能夠直接看以下代碼,我把註釋寫的也很詳細。
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); //建立autofac管理註冊類的容器實例 var builder = new ContainerBuilder(); //下面就須要爲這個容器註冊它能夠管理的類型 //builder的Register方法能夠經過多種方式註冊類型,以前在控制檯程序裏面也演示了好幾種方式了。 builder.RegisterType<Sqlserver>().As<IDataSource>(); //builder.RegisterType<DefaultController>().InstancePerDependency(); //使用Autofac提供的RegisterControllers擴展方法來對程序集中全部的Controller一次性的完成註冊 builder.RegisterControllers(Assembly.GetExecutingAssembly()); //生成具體的實例 var container = builder.Build(); //下面就是使用MVC的擴展 更改了MVC中的注入方式. DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); }須要解釋的是:
一、你們看下面的這句,這句的做用就是再MVC下面你必需要註冊一下Controller,不然沒有辦法注入。
//使用Autofac提供的RegisterControllers擴展方法來對程序集中全部的Controller一次性的完成註冊 builder.RegisterControllers(Assembly.GetExecutingAssembly());咱們經過使用RegisterControllers就能夠解決。那若是不用RegisterControllers 我就想一個個註冊的話怎麼弄?學技術有時候不要只管會用有的時候你也要理解人家提供的方法背後是怎麼作的。看到這裏你知道怎麼作麼?先考慮1分鐘,不要記得往下看。答案其實在上面講控制檯程序使用Autofac的時候已經講過了。好吧,我來詳細講一下,我先把以前控制檯程序的代碼貼出來。
public class DataSourceManager { IDataSource _ds; string Name; /// <summary> /// 根據傳入的類型動態建立對象 /// </summary> /// <param name="ds"></param>public DataSourceManager(stringname, IDataSource ds) { _ds = ds; Name = name; } public string GetData() { return Name + ":" + _ds.GetData(); } }這個類還記得嗎?不記得在看以前寫的文章。這個類有個IDataSource 做爲參數的構造方法。而後咱們在看一下使用時候的代碼?
var builder = new ContainerBuilder(); builder.RegisterType<DataSourceManager>(); builder.RegisterType<Sqlserver>().As<IDataSource>(); using (var container = builder.Build()) { var manager = container.Resolve<DataSourceManager>(new NamedParameter("name", "STONE劉先生")); Console.WriteLine(manager.GetData()); Console.ReadLine(); }
看到了嗎?container.Resolve<DataSourceManager>()這裏經過Resolve解析DataSourceManager實例,對於DataSourceManager類型,咱們爲Autofac提供了類型, 可是當Autofac建立DataSourceManager的實例, 調用它的構造函數的時候,它的構造函數須要提供一個IDataSource的實例做爲參數的,Autofac會在本身的容器裏,找註冊過IDataSource的實例,而且經過AsImplementedInterfaces()方法,指明爲接口IDataSource提供的實例。而後做爲建立DataSourceManager時,提供給構造函數的參數。這整個原理不知道這樣講你能聽懂嗎?
你們上面提出的若是不用RegisterControllers來,須要手動添加怎麼作?答案就是要寫若干個這個方法。
builder.RegisterType<DefaultController>().InstancePerDependency();注: DefaultController 控制器的名稱,你可要試着把RegisterControllers刪除掉,用上面的這句來嘗試一下。可是實際的項目中最好是用RegisterControllers。二、若是沒有寫builder.RegisterControllers<> ,並且控制器也沒有經過builder.RegisterType<>註冊, 你會看到以下的錯誤
整個MVC 使用autofac配置的工做就完成了。那接下來直接來看代碼裏面怎麼使用。
public class DefaultController : Controller { IDataSource ds; // 接口定義 構造函數注入 public DefaultController(IDataSource _ds) { ds = _ds; } // GET: Default public ActionResult Index() { //調用具體類的具體方法返回結果 賦值給ViewBag.Message ViewBag.Message = "STONE劉先生:" + ds.GetData(); return View(); } }整個功能請求的數據添加到ViewBag而後在頁面上面顯示出來,也比較簡單的。
運行後的效果:
成功了!
補充一下:
上面的列子演示的是構造函數注入,那看看可否改爲屬性注入。
看以下代碼,IDataSource 加上get;set就變成屬性了:
public class DefaultController : Controller { public IDataSource ds { get; set; } // 接口定義 構造函數注入 //public DefaultController(IDataSource _ds) //{ // ds = _ds; //} // GET: Default public ActionResult Index() { //調用具體類的具體方法返回結果 賦值給ViewBag.Message ViewBag.Message = "STONE劉先生:" + ds.GetData(); return View(); } }
若是如今任何地方都不改的狀況下,你看看會報什麼錯,是否是提醒ds爲null,那怎麼支持屬性注入呢! 我看了很久
builder.RegisterControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
把Global.asax裏面的這句改爲如上這句 就行了!
Autofac提供一個RegisterAssemblyTypes方法。它會去掃描全部的dll並把每一個類註冊爲它所實現的接口。既然可以自動注入,那麼接口和類的定義必定要有必定的規律。咱們能夠定義IDependency接口的類型,其餘任何的接口都須要繼承這個接口。好比
public interface IDependency { }
/// <summary> /// 業務邏輯實現——崗位管理 /// </summary> public class PostService : IDependency { public IPostService postService { get; set; } ........ }
自動注入原理說明:
首先咱們去找到全部Dll,再去找到實現了IDependency接口的類,而後使用RegisterAssemblyTypes進行注入。
Assembly[] assemblies = Directory.GetFiles(AppDomain.CurrentDomain.RelativeSearchPath, "*.dll").Select(Assembly.LoadFrom).ToArray(); //註冊全部實現了 IDependency 接口的類型 Type baseType = typeof(IDependency); builder.RegisterAssemblyTypes(assemblies) .Where(type => baseType.IsAssignableFrom(type) && !type.IsAbstract) .AsSelf().AsImplementedInterfaces() .PropertiesAutowired().InstancePerLifetimeScope(); //註冊MVC類型 builder.RegisterControllers(assemblies).PropertiesAutowired(); builder.RegisterFilterProvider(); var container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
結語
終於把AutoFac這篇完成了,有不對的地方,還但願各位能多多指點,共同窗習進步。初次寫系列文章,真的有點佩服在博客園分享的各位大牛了,這個真的是很耗費時間和精力的,之後更應該尊重別人的知識分享。
做者:STONE劉先生 出處:http://www.cnblogs.com/liupeng/
本文版權歸做者和博客園共有,歡迎轉載。未經做者贊成下,必須在文章頁面明顯標出原文連接及做者,不然保留追究法律責任的權利。若是您認爲這篇文章還不錯或者有所收穫,能夠點擊右下角的【推薦】按鈕,由於你的支持是我繼續寫做,分享的最大動力!