【asp.net core 系列】14 .net core 中的IOC

0.前言

經過前面幾篇,咱們瞭解到瞭如何實現項目的基本架構:數據源、路由設置、加密以及身份驗證。那麼在實現的時候,咱們還會遇到這樣的一個問題:當咱們業務類和數據源愈來愈多的時候,咱們沒法經過普通的構造對象的方法爲每一個實例進行賦值。同時,傳統意義上的賦值遇到底層切換或者其餘修改的時候,就須要修改大量的代碼,對改變不友好。爲了改變這種現狀,咱們基於面向接口編程,而後使用一些DI功能和IOC框架。程序員

圖片

1. IOC和DI

先來給你們解釋幾個概念,IOC全稱Inversion of Control,翻譯過來就是控制反轉,是面向對象編程的一種設計原則,用來下降代碼之間的耦合度。所謂的控制反轉簡單來說就是將類中屬性或者其餘參數的初始化交給其餘方處理,而不是直接使用構造函數。web

public class Demo1
{
}

public class Demo2
{
   public Demo1 demo;
}

對於以上簡單示例代碼中,在Demo2類中持有了一個Demo1的實例。若是按照以前的狀況來說,咱們會經過如下方法爲demo賦值:編程

// 方法一
public Demo1 demo = new Demo1();
// 方法二
public Demo2()
{
   demo = new Demo1();
}

這時候,若是Demo1變成下面的樣子:設計模式

public class Demo1
{
   public Demo1(Demo3 demo3)
   {
       // 隱藏
   }
}
public class Demo3
{
}

那麼,若是Demo2 沒有持有一個Demo3的實例對象,這時候建立Demo1的時候就須要額外構造一個Demo3。若是Demo3須要持有另一個類的對象,那麼Demo2中就須要多建立一個對象。最後就會發現這樣就陷入了一個構造「地獄」(我發明的詞,指這種爲了一個對象卻得構造一大堆其餘類型的對象)。架構

實際上,對於Demo2並不關心Demo1的實例對象是如何獲取的,甚至都不關心它是否是Demo1的子類或者接口實現類。我在示例中使用了類,但這裏能夠同步替換成Interface,替換以後,Demo2在調用Demo1的時候,還須要知道Demo1有實現類,以及實現類的信息。app

爲了解決這個問題,一些高明的程序員們提出了將對象的建立這一過程交給第三方去操做,而不是調用類來建立。因而乎,上述代碼就變成了:框架

public class Demo2
{
   public Demo1 Demo {get;set;}
   public Demo2(Demo1 demo)
   {
       Demo = demo;
   }
}

彷佛並無什麼變化?對於Demo2來講,Demo2今後再也不負責Demo1的建立,這個步驟交由Demo2的調用方去建立,Demo2今後從負責維護Demo1這個對象的大麻煩中解脫了。asp.net

但實際上構造地獄的問題仍是沒有解決,只不過是經過IOC的設計將這一步後移了。這時候,那些大神們想了想,不如開發一個框架這些實體對象吧。因此就出現了不少IOC框架:AutoFac、Sping.net、Unity等。ide

說到IOC就不得不提一下DI(Dependency Injection)依賴注入。所謂的依賴注入就是屬性對應實例經過構造函數或者使用屬性由第三方進行賦值。也就是最後Demo2的示例代碼中的寫法。函數

早期IOC和DI是指一種技術,後來開始肯定這是不一樣的描述。IOC描述的是一種設計模式,而DI是一種行爲。

2. 使用asp.net core的默認IOC

在以前的ASP.NET 框架中,微軟並無提供默認的IOC支持。在最新的asp.net core中微軟提供了一套IOC支持,該支持在命名空間:

Microsoft.Extensions.DependencyInjection

裏,在代碼中引用便可。

主要經過如下幾組方法實現:

public static IServiceCollection AddScoped<TService>(this IServiceCollection services) where TService : class;
public static IServiceCollection AddSingleton<TService>(this IServiceCollection services) where TService : class;
public static IServiceCollection AddTransient<TService>(this IServiceCollection services) where TService : class;

這裏只列出了這三組方法的一種重載版本。

這三組方法分別表明三種生命週期:

  • AddScored 表示對象的生命週期爲整個Request請求

  • AddTransient 表示每次從服務容器進行請求時建立的,適合輕量級、 無狀態的服務

  • AddSingleton 表示該對象在第一次從服務容器請求後獲取,以後就不會再次初始化了

這裏每組方法只介紹了一個版本,但實際上每一個方法都有如下幾個版本:

public static IServiceCollection AddXXX<TService>(this IServiceCollection services) where TService : class;
public static IServiceCollection AddXXX(this IServiceCollection services, Type serviceType, Type implementationType);
public static IServiceCollection AddXXX(this IServiceCollection services, Type serviceType, Func<IServiceProvider, object> implementationFactory);
public static IServiceCollection AddXXX<TService, TImplementation>(this IServiceCollection services)
           where TService : class
           where TImplementation : class, TService;
public static IServiceCollection AddXXX(this IServiceCollection services, Type serviceType);
public static IServiceCollection AddXXX<TService>(this IServiceCollection services, Func<IServiceProvider, TService> implementationFactory) where TService : class;
public static IServiceCollection AddXXX<TService, TImplementation>(this IServiceCollection services, Func<IServiceProvider, TImplementation> implementationFactory)
           where TService : class
           where TImplementation : class, TService;

其中:implementationFactory 表示經過一個Provider實現TService/TImplementation 的工廠方法。當方法指定了泛型的時候,會自動依據泛型參數獲取要注入的類型信息,若是沒有使用泛型則必須手動傳入參數類型。

asp.net core若是使用依賴注入的話,須要在Startup方法中設置,具體內容能夠參照如下:

public void ConfigureServices(IServiceCollection services)
{
   //省略其餘代碼
   services.AddScoped<ISysUserAuthRepository,SysUserAuthRepository>();
}

asp.net core 爲DbContext提供了不一樣的IOC支持,AddDbContext:

public static IServiceCollection AddDbContext<TContext>(
     this IServiceCollection serviceCollection,
     Action<DbContextOptionsBuilder> optionsAction = null,
     ServiceLifetime contextLifetime = ServiceLifetime.Scoped,
     ServiceLifetime optionsLifetime = ServiceLifetime.Scoped)
     where TContext : DbContext;

使用方法以下:

services.AddDbContext<DefaultContext>();

3. AutoFac 使用

理論上,asp.net core的IOC已經足夠好了,可是依舊原諒個人貪婪。若是有二三百個業務類須要我來設置的話,我寧願不使用IOC。由於那配置起來就是一場極其痛苦的過程。不過,可喜可賀的是AutoFac可讓我免受這部分的困擾。

這裏簡單介紹一下如何使用AutoFac做爲IOC管理:

cd Web  # 切換目錄到Web項目
dotnet package add Autofac.Extensions.DependencyInjection # 添加 AutoFac的引用

由於asp.net core 版本3更改了一些邏輯,AutoFac的引用方式發生了改變,如今不介紹以前版本的內容,以3爲主。使用AutoFac須要先在 Program類裏設置如下代碼:

public static IHostBuilder CreateHostBuilder(string[] args) =>
           Host.CreateDefaultBuilder(args)
           .UseServiceProviderFactory(new AutofacServiceProviderFactory()) // 添加這行代碼
           .ConfigureWebHostDefaults(webBuilder =>
           {
               webBuilder.UseStartup<Startup>();
           });

在Program類裏啓用AutoFac的一個Service提供工廠類。而後在Startup類裏添加以下方法:

public void ConfigureContainer(ContainerBuilder builder)
{
   builder.RegisterType<DefaultContext>().As<DbContext>()
               .WithParameter("connectStr","Data Source=./demo.db")
               .InstancePerLifetimeScope();


   builder.RegisterAssemblyTypes(Assembly.Load("Web"))
       .Where(t => t.BaseType.FullName.Contains("Filter"))
       .AsSelf();

   builder.RegisterAssemblyTypes(Assembly.Load("Domain"),
                   Assembly.Load("Domain.Implements"), Assembly.Load("Service"), Assembly.Load("Service.Implements"))
               .AsSelf()
               .AsImplementedInterfaces()
               .InstancePerLifetimeScope()
               .PropertiesAutowired();
}

修改:

public void ConfigureServices(IServiceCollection services)
{
   services.AddControllersWithViews(options =>
           {
               options.Filters.Add<UnitOfWorkFilterAttribute>();
           }).AddControllersAsServices();// 這行新增
   // 省略其餘
}

4. 總結

這一篇簡單介紹瞭如何在Asp.net Core中啓用IOC支持,並提供了兩種方式,能夠說是各有優劣。小夥伴們根據本身須要選擇。後續會爲你們詳細深刻AutoFac之類IOC框架的核心祕密。

相關文章
相關標籤/搜索