.Net Core 學習之路-AutoFac的使用

本文不介紹IoC和DI的概念,若是你對Ioc以前沒有了解的話,建議先去搜索一下相關的資料html

這篇文章將簡單介紹一下AutoFac的基本使用以及在asp .net core中的應用web

Autofac介紹

組件的三種註冊方式

  1. 反射
  2. 現成的實例(new)
  3. lambda表達式 (一個執行實例化對象的匿名方法)

下面是一些簡短的示例,我儘量多的列出來一些經常使用的註冊方式,同時在註釋中解釋下「組件」、「服務」等一些名詞的含義架構

// 建立註冊組件的builder
var builder = new ContainerBuilder();

//根據類型註冊組件 ConsoleLogger 暴漏服務:ILogger
builder.RegisterType<ConsoleLogger>().As<ILogger>();

//根據類型註冊組件 ConsoleLogger,暴漏其實現的全部服務(接口)
builder.RegisterType<ConsoleLogger>().AsImplementedInterfaces();

// 根據實例註冊組件 output  暴漏服務:TextWriter
var output = new StringWriter();
builder.RegisterInstance(output).As<TextWriter>();

//表達式註冊組件,這裏咱們是在構造函數時傳參->"musection"   暴漏服務:IConfigReader
builder.Register(c =new ConfigReader("mysection")).As<IConfigReader>();

//表達式註冊組件,解析時傳參
var service = scope.Resolve<IConfigReader>(
           new NamedParameter("section", "mysection"));     

//反射註冊組件,直接註冊了ConsoleLogger類(必須是具體的類),若是ConsoleLogger有多個構造函數,將會取參數最多的那個構造函數進行實例化
builder.RegisterType<ConsoleLogger>();

//反射註冊組件,手動指定構造函數,這裏指定了調用 MyComponent(ILogger log,IConfigReader config)的構造函數進行註冊
builder.RegisterType<MyComponent>()
 .UsingConstructor(typeof(ILogger), typeof(IConfigReader));  

 //註冊MySingleton類中的靜態變量"Instance",ExternallyOwned()函數指定本身控制實例的生命週期,而不是由autofac自動釋放
 builder.RegisterInstance(MySingleton.Instance).ExternallyOwned();

//一個組件暴漏兩個服務  
builder.RegisterType<CallLogger>().As<ILogger>().As<ICallInterceptor>(); 

//註冊當前程序集中以「Service」結尾的類
builder.RegisterAssemblyTypes(System.Reflection.Assembly.GetExecutingAssembly()).Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces();
//註冊"MyApp.Repository"程序集中全部的類
builder.RegisterAssemblyTypes(GetAssembly("MyApp.Repository")).AsImplementedInterfaces();
  
//構建一個容器完成註冊
var rootcontainer = builder.Build();

//能夠經過下面這種方式手動獲取IConfigReader 的實現類
//這種手動解析的方式須要 從生命週期做用域內獲取組件,以保證組件最終被釋放
//不要直接從根容器rootcontainer中解析組件,頗有可能會致使內存泄漏
using(var scope = rootcontainer.BeginLifetimeScope())
{
  var reader = scope.Resolve<IConfigReader>();
}

若是不止一個組件暴露了相同的服務, Autofac將使用最後註冊的組件做爲服務的提供方。 想要覆蓋這種行爲, 在註冊代碼後使用 PreserveExistingDefaults() 方法修改mvc

生命週期

using(var scope = rootcontainer.BeginLifetimeScope())app

上面的這段代碼建立了一個生命週期做用域asp.net

生命週期做用域是可釋放的,在做用域內解析的組件必定要保證在using以內使用或者最後手動調用組件的Dispose()函數ide

避免引用類的生命週期大於被引用類的生命週期 :如service 引用 repository 若是service的生命週期爲單例,repository的生命週期爲perrequest。service不會釋放,因此最終會形成相關的repository始終沒法釋放的狀況(Captive Dependencies函數

雖然咱們須要儘量的避免直接從根容器解析組件,但總有例外的狀況,對於非單例的組件,必定不要忘記調用組件的Dispose函數,實際上對於非單例的組件,從項目架構上來講,理論上應該是從構造函數注入進去的而不是手動解析。 須要手動解析的應該爲一些配置幫助類等ui

對於一個具體組件(類)的生命週期分爲如下幾種(後面的函數是autofac對應的函數):this

  • 每一個依賴一個實例(Instance Per Dependency) (默認) ----InstancePerDependency()
  • 單一實例(Single Instance) 單例 ----SingleInstance()
  • 每一個生命週期做用域一個實例(Instance Per Lifetime Scope)----InstancePerLifetimeScope()
  • 每一個匹配的生命週期做用域一個實例(Instance Per Matching Lifetime Scope)----InstancePerMatchingLifetimeScope()
  • 每一個請求一個實例(Instance Per Request) asp.net web請求----InstancePerRequest()
  • 每次被擁有一個實例(Instance Per Owned) ----InstancePerOwned()

若是你之前在傳統的ASP.NET MVC項目中用過autofac,須要注意一些區別:

  • .net Core中須要使用InstancePerLifetimeScope替代以前(傳統asp.net)的InstancePerRequest,保證每次HTTP請求只有惟一的依賴實例被建立。InstancePerRequest請求級別已經不存在了
  • .net Core中Web Api與Mvc的註冊方式同樣
  • .net Core中再也不須要註冊控制器,控制器由.net core建立,不歸autofac管理(除了控制器的構造函數),這也解釋了爲何再也不使用InstancePerRequest生命週期,可是能夠經過AddControllersAsServices()函數改變,想要深刻了解的能夠查看:https://www.strathweb.com/2016/03/the-subtle-perils-of-controller-dependency-injection-in-asp-net-core-mvc/

AutoFac 在asp .net core中的使用

在.net core 中使用autofac仍是比較簡單的,相比於傳統的asp.net web 項目,省去了不少步驟

引入nuget程序包:

  • Autofac
  • Autofac.Extensions.DependencyInjection

startup 中代碼:

public static IContainer AutofacContainer;
    // This method gets called by the runtime. Use this method to add services to the container.
    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        //註冊服務進 IServiceCollection
        services.AddMvc();
        ContainerBuilder builder = new ContainerBuilder();
        //將services中的服務填充到Autofac中.
        builder.Populate(services);
        //新模塊組件註冊
        builder.RegisterModule<DefaultModuleRegister>();
        //建立容器.
        AutofacContainer = builder.Build();
        //使用容器建立 AutofacServiceProvider 
        return new AutofacServiceProvider(AutofacContainer);
    }

上面代碼調用了builder的RegisterModule函數,這個函數須要傳入一個TModule的泛型,稱之爲autofac的模塊

模塊的功能就是把全部相關的註冊配置都放在一個類中,使代碼更易於維護和配置,下面展現了DefaultModuleRegister中的代碼

DefaultModuleRegister:

public class DefaultModuleRegister : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        //註冊當前程序集中以「Ser」結尾的類,暴漏類實現的全部接口,生命週期爲PerLifetimeScope
        builder.RegisterAssemblyTypes(System.Reflection.Assembly.GetExecutingAssembly()).Where(t => t.Name.EndsWith("Ser")).AsImplementedInterfaces().InstancePerLifetimeScope();
        builder.RegisterAssemblyTypes(System.Reflection.Assembly.GetExecutingAssembly()).Where(t => t.Name.EndsWith("Repository")).AsImplementedInterfaces().InstancePerLifetimeScope();
        //註冊全部"MyApp.Repository"程序集中的類
        //builder.RegisterAssemblyTypes(GetAssembly("MyApp.Repository")).AsImplementedInterfaces();
    }

    public static Assembly GetAssembly(string assemblyName)
    {
        var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(AppContext.BaseDirectory + $"{assemblyName}.dll");
        return assembly;
    }
}

Configure函數中能夠選擇性的加上程序中止時Autofac的釋放函數:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime appLifetime)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
        //程序中止調用函數
        appLifetime.ApplicationStopped.Register(() => { AutofacContainer.Dispose(); });
    }

Controller中代碼:

private IUserSer _user;
    private IUserSer _user2;
    public HomeController(IUserSer user, IUserSer user2)
    {
        _user = user;
        _user2 = user2;
    }
    public IActionResult Index()
    {
        using (var scope = Startup.AutofacContainer.BeginLifetimeScope())
        {
            IConfiguration config = scope.Resolve<IConfiguration>();
            IHostingEnvironment env = scope.Resolve<IHostingEnvironment>();
        }
        string name = _user.GetName();
        string name2 = _user2.GetName();
        return View();
    }

能夠看到,由於咱們將IServiceCollection中的服務填充到了autofac中了,因此如今能夠在任何位置經過AutoFac解析出來.net core默認注入的服務(IConfiguration,IHostingEnvironment等)了

正常項目使用中,咱們應該將AutofacContainer放在一個公共的類庫中以便各個工程都可調用

相關文章
相關標籤/搜索