在面向對象設計的軟件系統中,它的底層都是由N個對象構成的,各個對象之間經過相互合做,最終實現系統的業務邏輯。同時,對象之間的耦合關係是沒法避免的,也是必要的,這是協同工做的基礎。可是,伴隨着工業級應用的規模愈來愈龐大,對象之間的依賴關係也愈來愈複雜,常常會出現對象之間的多重依賴性關係,所以,架構師和設計師對於系統的分析和設計,將面臨更大的挑戰。對象之間耦合度太高的系統,必然會出現牽一髮而動全身的情形。數據庫
耦合關係不只會出如今對象與對象之間,也會出如今軟件系統的各模塊之間,以及軟件系統和硬件系統之間。如何下降系統之間、模塊之間和對象之間的耦合度,是軟件工程永遠追求的目標之一。爲了解決對象之間的耦合度太高的問題,軟件專家Michael Mattson 1996年提出了IOC理論,用來實現對象之間的「解耦」.微信
咱們來看下面的示例。架構
示例1mvc
public class EmailServiceapp {框架 public void SendMessage()函數 {性能 Console.WriteLine("經過電子郵件發送消息");ui }spa } public class MessageManager { private EmailService msgService; public MessageManager() { msgService=new EmailService(); } public void SendMsg() { msgService.SendMessage(); } } |
在示例1中,MessageManager 類依賴於EmailService類,二者之間存在耦合。 MessageManager類在構造函數內直接建立EmailService類的一個實例,換言之,MessageManager類精確的知道建立和使用了哪一種類型的服務。這種耦合表示了代碼的內部連接性。一個類知道與其交互的類的大量信息,咱們稱爲高耦合。這就會增長軟件修改的負擔,由於修改一個類極可能破壞依賴於它的另外一個類。
上面的代碼設計還有一個問題:當MessageManager不想採用Email方式發送消息,而是採用其它方式,如微信,那麼必須從新實現MessageManager類。
爲了下降組件之間的耦合程度,通常採起兩個獨立但相關的步驟:
(1)在兩塊代碼之間引入抽象層。
一般使用接口來表明兩個類之間的抽象層。如實例2所示。
public interface IMessageService { void SendMessage(); } public class EmailService : IMessageService { public void SendMessage() { Console.WriteLine("經過電子郵件發送消息"); } } public class MessageManager { private IMessageService msgService; public MessageManager() { msgService=new EmailService(); } public void SendMsg() { msgService.SendMessage(); } } |
(2)把選擇抽象實現的責任移到消費者的外部。
須要把EmailService類的建立移到MessageManager類的外面。
把依賴的建立移到使用這些依賴類的外部,這稱爲控制反轉模式,之因此這樣命名,是由於反轉的是依賴的建立,正由於如此,才消除了消費者類對依賴建立的控制。
控制反轉(Inversion of Control,即IOC)模式是抽象的:它只是表述應該從消費者類中移除依賴建立,而沒有表述如何實現。
依賴注入(Dependence Injection,即DI)是一種控制反轉的形式。其意思是自身對象中的內置對象是經過注入的方式進行建立。注入方式一般採用構造函數注入或屬性注入。
示例3
public class MessageManager { private IMessageService msgService; public MessageManager(IMessaageService service) { msgService=service; } public void SendMsg() { msgService.SendMessage(); } } |
示例3有一個顯著的優勢,它極大地簡化了構造函數的實現。組件老是指望建立它的類可以傳遞須要的依賴。而它只須要存儲IMessaageService接口的實例以便以後使用,不須要知道它本身的依賴項。另外一個有點就是需求的透明性。任何要建立MessageManager類實例的代碼都能查看構造函數,並精確的知道那些內容是使用該類必須的。
顧名思義,該方式是經過設置對象上的公共屬性而不是經過使用構造函數參數來注入依賴的。如示例4所示。
示例4
public class MessageManager { public IMessageService msgService {get; set;} public void SendMsg() { if (msgService ==null) { Throw new InvalidOperationException(); } msgService.SendMessage(); } } |
上面的兩種注入方式,都須要手動提供所需的依賴項,也就意味着都須要咱們知道如何來知足每一部分的須要。
依賴注入容器是一個使依賴解析變得簡單的一種方式。
Autofac是一款IOC容器框架,比較於其餘的IOC框架,如Spring.NET,Unity,Castle等等框架,它很輕量級,性能上也很高。
經過VS中的NuGet來加載AutoFac,引入成功後引用就會出現Autofac。如圖1所示。
圖1-1 獲取Autofac
示例5中,數據層有兩個類,一個是Oracle 一個是SQLSERVER。咱們在使用的時候能夠選擇調用那個數據庫。經過Autofac來完成構造函數注入。
示例5
/// <summary> /// 數據源操做接口 /// </summary> public interface IDataSource { /// <summary> /// 獲取數據 /// </summary> /// <returns></returns> string GetData(); } /// <summary> /// SQLSERVER數據庫 /// </summary> public class Sqlserver : IDataSource { public string GetData() { return "經過SQLSERVER獲取數據"; } } /// <summary> /// ORACLE數據庫 /// </summary> public class Oracle : IDataSource { public string GetData() { return "經過Oracle獲取數據"; } } /// <summary> /// 數據源管理類 /// </summary public class DataSourceManager IDataSource _ds; public string GetData()
static void Main(string[] args) { //獲取Autofac容器構造器 var builder = new ContainerBuilder(); // 註冊類型 builder.RegisterType<DataSourceManager>(); // 註冊實例(即將SqlServer類型註冊爲IDataSource的實例 builder.RegisterType<Sqlserver>().As<IDataSource>(); // 由構造器建立Ioc容器 using (var container = builder.Build()) { // 解析實例 var manager = container.Resolve<DataSourceManager>(); Console.WriteLine(manager.GetData()); Console.ReadLine(); } } |
示例5所示代碼展現了Autofac的基本使用,在實際開發中,還有不少其它的用法。
(1) AsImplementedInterfaces (註冊多個接口)
在不少狀況下,一個類可能實現了多個接口,若是按照實例5的寫法,咱們須要爲每個接口都註冊實例,很是繁瑣。這時候就可使用AsImplementedInterfaces() 方法,能夠把一個類註冊給它實現的所有接口。代碼以下:
builder.RegisterType<Sqlserver>().AsImplementedInterfaces()
(2)利用反射註冊
在實際開發中,每每會有不少接口須要註冊,這時咱們能夠經過反射的方式將其所有註冊。代碼以下:
//加載實現類的程序集
Assembly asm = Assembly.Load("DAL.BLL");
builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces();
咱們能夠在配置文件中將程序集信息在<appSettings>節點中進行配置,來減小對程序集的引用,完全解除耦合。
(3) PropertiesAutowired (利用屬性注入)
上述經過Autofac進行注入都是針對構造函數進行的註冊,若是使用屬性進行註冊須要使用PropertiesAutowired()方法。
//加載實現類的程序集
Assembly asm = Assembly.Load("DAL.BLL");
builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces().PropertiesAutowired();
(4)一個接口多個實現
一個接口有多個實現類的狀況,如實例5中,SqlServer 和 Oracle 都實現了IDataSource接口,如有同時註冊,須要使用Resolve<IEnumerable<IAnimalBLL>>()便可。如實例6所示。
實例6
Assembly asm = Assembly.Load("DAL.BLL"); builder.RegisterAssemblyTypes(asm).AsImplementedInterfaces(); using (var container = builder.Build()) { IEnumerable<IDataSource> dals = container.Resolve<IEnumerable<IDataSource>>(); foreach(var dal in dals) { Console.WriteLine(dal.GetData()); } Console.ReadLine(); }
|
也能夠經過Named()方法在指定要想獲取的類型。如示例7所示。
示例7
//獲取Autofac容器構造器 var builder = new ContainerBuilder(); // 註冊類型 builder.RegisterType<DataSourceManager>(); // 註冊實例 builder.RegisterType<Sqlserver>().Named<IDataSource>("SqlServer"); builder.RegisterType<Sqlserver>().Named<IDataSource>("Oracle"); // 由構造器建立Ioc容器 using (var container = builder.Build()) { // 解析實例 var manager = container.ResolveNamed<DataSourceManager>("SqlServer"); Console.WriteLine(manager.GetData()); Console.ReadLine(); } |
經過配置實現Autofac 須要添加對Autofac.Configuration.dll的引用。
示例8
配置文件 <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>
C#代碼 var builder = new ContainerBuilder(); builder.RegisterType<DataSourceManager>(); builder.RegisterModule( new ConfigurationSettingsReader("autofac" ));
|
在.net Mvc中使用Autofac,原理和上述講解同樣,區別在於須要經過Nuget多安裝一個程序集 Autofac.Mvc5
步驟1
在MVC項目中添加對 Autofac 和 Autofac.Mvc5 的引用。
步驟2
建立配置類。在此咱們經過屬性注入的方式進行注入,如示例9所示。
示例9
public class AutofacConfig { public static void Register() { var builder = new ContainerBuilder(); //加載當前程序集 Assembly controllerAss = Assembly.GetExecutingAssembly(); //Assembly controllerAss = Assembly.Load("OA.Web"); //註冊全部的Controller builder.RegisterControllers(controllerAss).PropertiesAutowired();
//註冊數據訪問層程序集 Assembly resAss = Assembly.Load("OA.DAL"); builder.RegisterTypes(resAss.GetTypes()).AsImplementedInterfaces(); //註冊業務層程序集 Assembly bllAss = Assembly.Load("OA.BLL"); builder.RegisterTypes(bllAss.GetTypes()).AsImplementedInterfaces();
var container = builder.Build(); //當mvc建立controller對象的時候,都是由AutoFac爲咱們建立 //Controller對象 DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); } } |
示例9中對數據層和業務層的注入請根據實際狀況進行註冊,不是必須的。
步驟3
在Global.asax 的Application_Start() 註冊本身的控制器類
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
//Autofac配置
AutofacConfig.Register();
}
至此,在MVC中使用 Autofac 的配置步驟完成。
在Controller中的使用以下:
public class HomeController :Controller
{
public IDataSource dataSource { get ; set; }
public ActionResult Index( )
{
var data = dataSource.GetData( );
return View(data);
}
}
dataSource 屬性會被 Autofac 自動實例化。