一步一步學EF系列【6、IOC 之AutoFac】


前言 

     之前的前5篇作為EF方面的基礎篇,后面我們將使用MVC+EF 並且使用IOC ,Repository,UnitOfWork,DbContext來整體來學習。因為后面要用到IOC,所以本篇先單獨先學習一下IOC,我們本本文單獨主要學習Autofac,其實對於Autofac我也是邊學邊記錄。不對的地方,也希望大家多多指導。

個人在學習過程中參考博客:

AutoFac文檔:http://www.cnblogs.com/wolegequ/archive/2012/06/09/2543487.html

AutoFac使用方法總結:Part I:http://niuyi.github.io/blog/2012/04/06/autofac-by-unit-test/

為什么使用AutoFac?

       Autofac是.NET領域最為流行的IOC框架之一,傳說是速度最快的一個:

優點:

  • 它是C#語言聯系很緊密,也就是說C#里的很多編程方式都可以為Autofac使用,例如可以用Lambda表達式注冊組件
  • 較低的學習曲線,學習它非常的簡單,只要你理解了IoC和DI的概念以及在何時需要使用它們
  • XML配置支持
  • 自動裝配
  • 與Asp.Net MVC 集成
  • 微軟的Orchad開源程序使用的就是Autofac,從該源碼可以看出它的方便和強大

上面的優點我也是拷的別人文章里面的,上面的這個幾乎所有講Autofac博文都會出現的。這個也是首次學習,所以我們還是記錄的細一點。

怎么使用Autofac

通過VS中的NuGet來加載AutoFac,引入成功后引用就會出現Autofac。

imageimage

     1、我們做一個簡單的例子先用一下

就拿數據訪問來做案例把,一個數據請求有兩個類,一個是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> 
/// 數據源管理類
/// </summary
public 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();
}
image運行后的代碼。
4)IContainer.ResolveNamed<IDAL>(string name):解析某個接口的“命名實例”。例如上面的實例最后一行代碼
container.ResolveNamed<IDataSource>("Oracel" ); 就是解析IDataSource的命名實例 Oracel。
5imagebuilder.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時給其賦值,這個就是給你定義的方法的參數傳值。
        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();
        }
我把DataSourceManager的構造方法加了個name參數,然后我調用的時候:
var manager = container.Resolve<DataSourceManager>(new NamedParameter("name", "STONE劉先生"));

運行后的代碼:

image

 

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下面使用Autofac

   引用和上面的控制台程序的原理是一模一樣的。但是區別就在於要多添加一個引用

imageimage

     案例還是用上面的案例。我是把之前的接口和類拷貝到MVC項目里面作為下面演示。代碼就不在寫出來了,一模一樣的。

1、首先在函數Application_Start() 注冊自己的控制器類

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));

        }

需要解釋的是:

1、大家看下面的這句,這句的作用就是再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(string
 name, 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。

2、如果沒有寫builder.RegisterControllers<> ,而且控制器也沒有通過builder.RegisterType<>注冊, 你會看到如下的錯誤

image

     整個MVC 使用autofac配置的工作就完成了。那接下來直接來看代碼里面怎么使用。

2、   添加控制器,並注入依賴代碼

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然后在頁面上面顯示出來,也比較簡單的。

運行后的效果:

image

成功了!

補充一下:

上面的列子演示的是構造函數注入,那看看能否改成屬性注入。

看如下代碼,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這篇完成了,有不對的地方,還希望各位能多多指點,共同學習進步。初次寫系列文章,真的有點佩服在博客園分享的各位大牛了,這個真的是很耗費時間和精力的,以后更應該尊重別人的知識分享。

 

     大家也可以加入QQ群進行交流(435498053)。輕松做生意外貿軟件

 

 

作者:STONE劉先生 出處:http://www.cnblogs.com/liupeng/

本文版權歸作者和博客園共有,歡迎轉載。未經作者同意下,必須在文章頁面明顯標出原文鏈接及作者,否則保留追究法律責任的權利。
如果您認為這篇文章還不錯或者有所收獲,可以點擊右下角的【推薦】按鈕,因為你的支持是我繼續寫作,分享的最大動力!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM