日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > asp.net >内容正文

asp.net

用Scrutor来简化ASP.NET Core的DI注册

發(fā)布時(shí)間:2023/12/20 asp.net 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用Scrutor来简化ASP.NET Core的DI注册 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

  • 背景
  • Scrutor簡(jiǎn)介
  • Scrutor的簡(jiǎn)單使用
    • 注冊(cè)接口的實(shí)現(xiàn)類(lèi)
    • 注冊(cè)類(lèi)自身
    • 重復(fù)注冊(cè)處理策略
  • 總結(jié)
  • 相關(guān)文章

背景

在我們編寫(xiě)ASP.NET Core代碼的時(shí)候,總是離不開(kāi)依賴注入這東西。而且對(duì)于這一塊,我們有非常多的選擇,比如:M$ 的DI,Autofac,Ninject,Windsor 等。

由于M$自帶了一個(gè)DI框架,所以一般情況下都會(huì)優(yōu)先使用。雖說(shuō)功能不是特別全,但也基本滿足使用了。

正常情況下(包括好多示例代碼),在要注冊(cè)的服務(wù)數(shù)量比較少時(shí),我們會(huì)選擇一個(gè)一個(gè)的去注冊(cè)。

好比下面的示例:

services.AddTransient<IUserRepository, UserRepository>(); services.AddTransient<IUserService, UserService>();

在數(shù)量小于5個(gè)的時(shí)候,這樣的做法還可以接受,但是,數(shù)量一多,還這樣子秀操作,可就有點(diǎn)接受不了了。

可能會(huì)經(jīng)常出現(xiàn)這樣的問(wèn)題,新加了一個(gè)東西,忘記在Startup上面注冊(cè),下一秒得到的就是類(lèi)似下面的錯(cuò)誤:

System.InvalidOperationException: Unable to resolve service for type 'ScrutorTest.IProductRepository' while attempting to activate 'ScrutorTest.Controllers.ValuesController'.at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)at lambda_method(Closure , IServiceProvider , Object[] )at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.<CreateActivator>b__0(ControllerContext controllerContext)at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

這樣一來(lái)一回,其實(shí)也是挺浪費(fèi)時(shí)間的。

為了避免這種情況,我們往往會(huì)根據(jù)規(guī)律在注冊(cè)的時(shí)候,用反射進(jìn)行批量注冊(cè),后面按照對(duì)應(yīng)的規(guī)律去寫(xiě)業(yè)務(wù)代碼,就可以避免上面這種問(wèn)題了。

對(duì)于這個(gè)問(wèn)題,本文將介紹一個(gè)擴(kuò)展庫(kù),來(lái)幫我們簡(jiǎn)化這些操作。

Scrutor簡(jiǎn)介

Scrutor是 Kristian Hellang 大神寫(xiě)的一個(gè)基于Microsoft.Extensions.DependencyInjection的一個(gè)擴(kuò)展庫(kù),主要是為了簡(jiǎn)化我們對(duì)DI的操作。

Scrutor主要提供了兩個(gè)擴(kuò)展方法給我們使用,一個(gè)是Scan,一個(gè)是Decorate。

本文主要講的是Scan這個(gè)方法。

Scrutor的簡(jiǎn)單使用

注冊(cè)接口的實(shí)現(xiàn)類(lèi)

這種情形應(yīng)該是我們用的最多的一種,所以優(yōu)先來(lái)說(shuō)這種情況。

假設(shè)我們有下面幾個(gè)接口和實(shí)現(xiàn)類(lèi),

public interface IUserService { } public class UserService : IUserService { }public interface IUserRepository { } public class UserRepository : IUserRepository { }public interface IProductRepository { } public class ProductRepository : IProductRepository { }

現(xiàn)在我們只需要注冊(cè)UserRepository和ProductRepository,

services.Scan(scan => scan.FromAssemblyOf<Startup>().AddClasses(classes => classes.Where(t=>t.Name.EndsWith("repository",StringComparison.OrdinalIgnoreCase))).AsImplementedInterfaces().WithTransientLifetime());

簡(jiǎn)單解釋一下,上面的代碼做了什么事:

  • FromAssemblyOf<Startup> 表示加載Startup這個(gè)類(lèi)所在的程序集
  • AddClasses 表示要注冊(cè)那些類(lèi),上面的代碼還做了過(guò)濾,只留下了以 repository 結(jié)尾的類(lèi)
  • AsImplementedInterfaces 表示將類(lèi)型注冊(cè)為提供其所有公共接口作為服務(wù)
  • WithTransientLifetime 表示注冊(cè)的生命周期為 Transient
  • 如果了解過(guò)Autofac的朋友,看到這樣的寫(xiě)法應(yīng)該很熟悉。

    對(duì)于上面的例子,它等價(jià)于下面的代碼

    services.AddTransient<IUserRepository, UserRepository>(); services.AddTransient<IProductRepository, ProductRepository>();

    如果我們?cè)谧?cè)完成后,想看一下我們自己注冊(cè)的信息,可以加上下面的代碼:

    var list = services.Where(x => x.ServiceType.Namespace.Equals("ScrutorTest", StringComparison.OrdinalIgnoreCase)).ToList();foreach (var item in list) {Console.WriteLine($"{item.Lifetime},{item.ImplementationType},{item.ServiceType}"); }

    運(yùn)行dotnet run之后,可以看到下面的輸出

    Singleton,ScrutorTest.UserRepository,ScrutorTest.IUserRepository Singleton,ScrutorTest.ProductRepository,ScrutorTest.IProductRepository

    這個(gè)時(shí)候,如果我們加了一個(gè) IOrderRepository 和 OrderRepostity , 就不需要在Startup上面多寫(xiě)一行注冊(cè)代碼了,Scrutor已經(jīng)幫我們自動(dòng)處理了。

    接下來(lái),我們需要把UserService也注冊(cè)進(jìn)去,我們完全可以照葫蘆畫(huà)瓢了。

    services.Scan(scan => scan.FromAssemblyOf<Startup>().AddClasses(classes => classes.Where(t=>t.Name.EndsWith("repository",StringComparison.OrdinalIgnoreCase))).AsImplementedInterfaces().WithTransientLifetime());services.Scan(scan => scan.FromAssemblyOf<Startup>().AddClasses(classes => classes.Where(t => t.Name.EndsWith("service", StringComparison.OrdinalIgnoreCase))).AsImplementedInterfaces().WithTransientLifetime());

    也可以略微簡(jiǎn)單一點(diǎn)點(diǎn),一個(gè)scan里面搞定所有

    services.Scan(scan => scan.FromAssemblyOf<Startup>().AddClasses(classes => classes.Where(t=>t.Name.EndsWith("repository",StringComparison.OrdinalIgnoreCase))).AsImplementedInterfaces().WithTransientLifetime().AddClasses(classes => classes.Where(t => t.Name.EndsWith("service", StringComparison.OrdinalIgnoreCase))).AsImplementedInterfaces().WithScopedLifetime()//換一下生命周期);

    這個(gè)時(shí)候結(jié)果如下:

    Transient,ScrutorTest.UserRepository,ScrutorTest.IUserRepository Transient,ScrutorTest.ProductRepository,ScrutorTest.IProductRepository Scoped,ScrutorTest.UserService,ScrutorTest.IUserService

    雖然效果一樣,但是總想著有沒(méi)有一些更簡(jiǎn)單的方法。

    很多時(shí)候,我們寫(xiě)一些接口和實(shí)現(xiàn)類(lèi)的時(shí)候,都會(huì)根據(jù)這樣的習(xí)慣來(lái)命名,定義一個(gè)接口IClass,它的實(shí)現(xiàn)類(lèi)就是Class。

    針對(duì)這種情形,Scrutor提供了一個(gè)簡(jiǎn)便的方法來(lái)幫助我們處理。

    使用 AsMatchingInterface 方法就可以很輕松的幫我們處理注冊(cè)好對(duì)應(yīng)的信息。

    services.Scan(scan => scan.FromAssemblyOf<Startup>().AddClasses().AsMatchingInterface().WithTransientLifetime());

    這個(gè)時(shí)候會(huì)輸出下面的結(jié)果:

    Transient,ScrutorTest.UserService,ScrutorTest.IUserService Transient,ScrutorTest.UserRepository,ScrutorTest.IUserRepository Transient,ScrutorTest.ProductRepository,ScrutorTest.IProductRepository

    當(dāng)然這種方法也有對(duì)應(yīng)的缺點(diǎn),那就是對(duì)生命周期的控制。舉個(gè)例子,有兩大類(lèi),一大類(lèi)要Transient,一大類(lèi)要Scoped,這個(gè)時(shí)候,我們也只能過(guò)濾掉部分內(nèi)容才能注冊(cè) 。

    需要根據(jù)自身的情況來(lái)選擇是否要使用這個(gè)方法,或者什么時(shí)候使用這個(gè)方法。

    注冊(cè)類(lèi)自身

    有時(shí)候,我們建的一些類(lèi)是沒(méi)有實(shí)現(xiàn)接口的,就純粹是在“裸奔”的那種,然后直接用單例的方式來(lái)調(diào)用。

    Scrutor也提供了方法AsSelf來(lái)處理這種情形。

    來(lái)看下面這段代碼。

    services.Scan(scan => scan.AddTypes(typeof(MyClass)).AsSelf().WithSingletonLifetime());

    這里和前面的注冊(cè)代碼有一點(diǎn)點(diǎn)差異。

    AddTypes是直接加載具體的某個(gè)類(lèi)或一批類(lèi),這個(gè)的作用可以認(rèn)為和FromXxx是一樣的。

    它等價(jià)于下面的代碼

    services.AddSingleton<MyClass>();

    相對(duì)來(lái)說(shuō)批量操作的時(shí)候還是有點(diǎn)繁鎖,因?yàn)樾枰衙總€(gè)類(lèi)型都扔進(jìn)去,我們不可能事先知道所有的類(lèi)。

    下面的方法可以把MyClass所在的程序集的類(lèi)都注冊(cè)了。

    services.Scan(scan => scan.FromAssemblyOf<MyClass>().AddClasses().AsSelf().WithSingletonLifetime());

    這樣的做法也有一個(gè)缺點(diǎn),會(huì)造成部分我們不想讓他注冊(cè)的,也注冊(cè)進(jìn)去了。

    過(guò)濾一下或者規(guī)范一下自己的結(jié)構(gòu),就可以處理這個(gè)問(wèn)題了。

    重復(fù)注冊(cè)處理策略

    還有一個(gè)比較常見(jiàn)的情形是,重復(fù)注冊(cè),即同一個(gè)接口,有多個(gè)不同的實(shí)現(xiàn)。

    Scrutor提供了三大策略,AppendSkipReplace。 Append是默認(rèn)行為,就是疊加。

    下面來(lái)看這個(gè)例子

    public interface IDuplicate { } public class FirstDuplicate : IDuplicate { } public class SecondDuplicate : IDuplicate { } services.Scan(scan => scan.FromAssemblyOf<Startup>() .AddClasses(classes=>classes.AssignableTo<IDuplicate>()).AsImplementedInterfaces().WithTransientLifetime() );

    這個(gè)時(shí)候的輸出如下

    Transient,ScrutorTest.FirstDuplicate,ScrutorTest.IDuplicate Transient,ScrutorTest.SecondDuplicate,ScrutorTest.IDuplicate

    下面我們用Skip策略來(lái)替換默認(rèn)的策略

    services.Scan(scan => scan.FromAssemblyOf<Startup>() .AddClasses(classes=>classes.AssignableTo<IDuplicate>())//手動(dòng)高亮.UsingRegistrationStrategy(RegistrationStrategy.Skip).AsImplementedInterfaces().WithTransientLifetime() );

    這個(gè)時(shí)候的輸出如下

    Transient,ScrutorTest.FirstDuplicate,ScrutorTest.IDuplicate

    可見(jiàn)得到的結(jié)果確實(shí)沒(méi)有了第二個(gè)注冊(cè)。

    總結(jié)

    Scrutor的Scan方法確實(shí)很方便,可以讓我們很容易的擴(kuò)展M$ 的DI。

    當(dāng)然Scrutor還有其他的用法,詳細(xì)的可以參考它的Github頁(yè)面。

    相關(guān)文章

    Introducing Scrutor - Convention based registration for Microsoft.Extensions.DependencyInjection

    Using Scrutor to automatically register your services with the ASP.NET Core DI container

    轉(zhuǎn)載于:https://www.cnblogs.com/catcher1994/p/10316928.html

    總結(jié)

    以上是生活随笔為你收集整理的用Scrutor来简化ASP.NET Core的DI注册的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。