亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

.NET8 依賴注入

 更新時(shí)間:2023年12月04日 09:25:35   作者:xiaolipro  
依賴注入是一種設(shè)計(jì)模式,用于解耦組件(服務(wù))之間的依賴關(guān)系,它通過將依賴關(guān)系的創(chuàng)建和管理交給外部容器來實(shí)現(xiàn),而不是在組件(服務(wù))內(nèi)部直接創(chuàng)建依賴對(duì)象,本文介紹.NET8 依賴注入的相關(guān)知識(shí),感興趣的朋友一起看看吧

依賴注入(Dependency Injection,簡(jiǎn)稱DI)是一種設(shè)計(jì)模式,用于解耦組件(服務(wù))之間的依賴關(guān)系。它通過將依賴關(guān)系的創(chuàng)建和管理交給外部容器來實(shí)現(xiàn),而不是在組件(服務(wù))內(nèi)部直接創(chuàng)建依賴對(duì)象。

? 咱就是通過 IServiceCollection 和 IServiceProvider 來實(shí)現(xiàn)的,他們直接被收入到了runtime libraries,在整個(gè).NET平臺(tái)下通用!

1.1 ServiceCollection

? IServiceCollection 本質(zhì)是一個(gè) ServiceDescriptor 而 ServiceDescriptor 則是用于描述服務(wù)類型,實(shí)現(xiàn)和生命周期

public interface IServiceCollection : 
    IList<ServiceDescriptor>,
    ICollection<ServiceDescriptor>,
    IEnumerable<ServiceDescriptor>,
    IEnumerable;

? 官方提供一些列拓展幫助我們向服務(wù)容器中添加服務(wù)描述,具體在 ServiceCollectionServiceExtensions

builder.Services.AddTransient<StudentService>();
builder.Services.AddKeyedTransient<IStudentRepository, StudentRepository>("a");
builder.Services.AddKeyedTransient<IStudentRepository, StudentRepository2>("b");
builder.Services.AddTransient<TransientService>();
builder.Services.AddScoped<ScopeService>();
builder.Services.AddSingleton<SingletonService>();

1.2 ServiceProvider

? IServiceProvider 定義了一個(gè)方法 GetService,幫助我們通過給定的服務(wù)類型,獲取其服務(wù)實(shí)例

public interface IServiceProvider
{
  object? GetService(Type serviceType);
}

? 下面是 GetService 的默認(rèn)實(shí)現(xiàn)(如果不給定engine scope,則默認(rèn)是root)

public object? GetService(Type serviceType) => GetService(ServiceIdentifier.FromServiceType(serviceType), Root);

? 也就是

internal object? GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
{
    if (_disposed)
    {
        ThrowHelper.ThrowObjectDisposedException();
    }
    // 獲取服務(wù)標(biāo)識(shí)符對(duì)應(yīng)的服務(wù)訪問器
    ServiceAccessor serviceAccessor = _serviceAccessors.GetOrAdd(serviceIdentifier, _createServiceAccessor);
    // 執(zhí)行解析時(shí)的hock
    OnResolve(serviceAccessor.CallSite, serviceProviderEngineScope);
    DependencyInjectionEventSource.Log.ServiceResolved(this, serviceIdentifier.ServiceType);
    // 通過服務(wù)訪問器提供的解析服務(wù),得到服務(wù)實(shí)例
    object? result = serviceAccessor.RealizedService?.Invoke(serviceProviderEngineScope);
    System.Diagnostics.Debug.Assert(result is null || CallSiteFactory.IsService(serviceIdentifier));
    return result;
}

? 其中,服務(wù)標(biāo)識(shí)符 ServiceIdentifier 其實(shí)就是包了一下服務(wù)類型,和服務(wù)Key(為了.NET8的鍵化服務(wù))

internal readonly struct ServiceIdentifier : IEquatable<ServiceIdentifier>
{
    public object? ServiceKey { get; }
    public Type ServiceType { get; }
}

? 顯而易見的,我們的服務(wù)解析是由 serviceAccessor.RealizedService 提供,而創(chuàng)建服務(wù)訪問器 serviceAccessor 只有一個(gè)實(shí)現(xiàn) CreateServiceAccessor

private ServiceAccessor CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
{
    // 通過 CallSiteFactory 獲取服務(wù)的調(diào)用點(diǎn)(CallSite),這是服務(wù)解析的一個(gè)表示形式。
    ServiceCallSite? callSite = CallSiteFactory.GetCallSite(serviceIdentifier, new CallSiteChain());
    // 如果調(diào)用站點(diǎn)不為空,則繼續(xù)構(gòu)建服務(wù)訪問器。
    if (callSite != null)
    {
        DependencyInjectionEventSource.Log.CallSiteBuilt(this, serviceIdentifier.ServiceType, callSite);
        // 觸發(fā)創(chuàng)建調(diào)用站點(diǎn)的相關(guān)事件。
        OnCreate(callSite);
        // 如果調(diào)用站點(diǎn)的緩存位置是根(Root),即表示這是一個(gè)單例服務(wù)。
        if (callSite.Cache.Location == CallSiteResultCacheLocation.Root)
        {
            // 直接拿緩存內(nèi)容
            object? value = CallSiteRuntimeResolver.Instance.Resolve(callSite, Root);
            return new ServiceAccessor { CallSite = callSite, RealizedService = scope => value };
        }
        // 通過引擎解析
        Func<ServiceProviderEngineScope, object?> realizedService = _engine.RealizeService(callSite);
        return new ServiceAccessor { CallSite = callSite, RealizedService = realizedService };
    }
    // 如果調(diào)用點(diǎn)為空,則它的實(shí)現(xiàn)服務(wù)函數(shù)總是返回 null。
    return new ServiceAccessor { CallSite = callSite, RealizedService = _ => null };
}

1.2.1 ServiceProviderEngine

? ServiceProviderEngine 是服務(wù)商解析服務(wù)的執(zhí)行引擎,它在服務(wù)商被初始化時(shí)建立。有兩種引擎,分別是動(dòng)態(tài)引擎運(yùn)行時(shí)引擎,在 NETFRAMEWORK || NETSTANDARD2_0 默認(rèn)使用動(dòng)態(tài)引擎。

        private ServiceProviderEngine GetEngine()
        {
            ServiceProviderEngine engine;
#if NETFRAMEWORK || NETSTANDARD2_0
            engine = CreateDynamicEngine();
#else
            if (RuntimeFeature.IsDynamicCodeCompiled && !DisableDynamicEngine)
            {
                engine = CreateDynamicEngine();
            }
            else
            {
                // Don't try to compile Expressions/IL if they are going to get interpreted
                engine = RuntimeServiceProviderEngine.Instance;
            }
#endif
            return engine;
            [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode",
                Justification = "CreateDynamicEngine won't be called when using NativeAOT.")] // see also https://github.com/dotnet/linker/issues/2715
            ServiceProviderEngine CreateDynamicEngine() => new DynamicServiceProviderEngine(this);
        }

? 由于.NET Aot技術(shù)與dynamic技術(shù)沖突,因此Aot下只能使用運(yùn)行時(shí)引擎,但動(dòng)態(tài)引擎在大多情況下仍然是默認(rèn)的。

動(dòng)態(tài)引擎使用了emit技術(shù),這是一個(gè)動(dòng)態(tài)編譯技術(shù),而aot的所有代碼都需要在部署前編譯好,因此運(yùn)行時(shí)無法生成新的代碼。那運(yùn)行時(shí)引擎主要使用反射,目標(biāo)是在不犧牲太多性能的情況下,提供一個(gè)在aot環(huán)境中可行的解決方案。

? 我們展開動(dòng)態(tài)引擎來看看它是如何解析服務(wù)的。

public override Func<ServiceProviderEngineScope, object?> RealizeService(ServiceCallSite callSite)
{
    // 定義一個(gè)局部變量來跟蹤委托被調(diào)用的次數(shù)
    int callCount = 0;
    return scope =>
    {
        // 當(dāng)委托被調(diào)用時(shí),先使用CallSiteRuntimeResolver.Instance.Resolve方法來解析服務(wù)。這是一個(gè)同步操作,確保在編譯優(yōu)化之前,服務(wù)可以被正常解析。
        var result = CallSiteRuntimeResolver.Instance.Resolve(callSite, scope);
        // 委托第二次被調(diào)用,通過UnsafeQueueUserWorkItem在后臺(tái)線程上啟動(dòng)編譯優(yōu)化
        if (Interlocked.Increment(ref callCount) == 2)
        {
            // 將一個(gè)工作項(xiàng)排隊(duì)到線程池,但不捕獲當(dāng)前的執(zhí)行上下文。
            _ = ThreadPool.UnsafeQueueUserWorkItem(_ =>
            {
                try
                {
                    // 用編譯優(yōu)化后的委托替換當(dāng)前的服務(wù)訪問器,主要用到emit/expression技術(shù)
                    _serviceProvider.ReplaceServiceAccessor(callSite, base.RealizeService(callSite));
                }
                catch (Exception ex)
                {
                    DependencyInjectionEventSource.Log.ServiceRealizationFailed(ex, _serviceProvider.GetHashCode());
                    Debug.Fail($"We should never get exceptions from the background compilation.{Environment.NewLine}{ex}");
                }
            },
            null);
        }
        return result;
    };
}

這個(gè)實(shí)現(xiàn)的關(guān)鍵思想是,第一次解析服務(wù)時(shí)使用一個(gè)簡(jiǎn)單的運(yùn)行時(shí)解析器,這樣可以快速返回服務(wù)實(shí)例。然后,當(dāng)服務(wù)再被解析,它會(huì)在后臺(tái)線程上啟動(dòng)一個(gè)編譯過程,生成一個(gè)更高效的服務(wù)解析委托。一旦編譯完成,新的委托會(huì)替換掉原來的委托,以后的服務(wù)解析將使用這個(gè)新的、更高效的委托。這種方法可以在不影響應(yīng)用程序啟動(dòng)時(shí)間的情況下,逐漸優(yōu)化服務(wù)解析的性能。

1.2.2 ServiceProviderEngineScope

? ServiceProviderEngineScope 閃亮登場(chǎng),他是我們服務(wù)商的代言人,從定義不難看出他對(duì)外提供了服務(wù)商所具備的一切能力

internal sealed class ServiceProviderEngineScope : IServiceScope, IServiceProvider, IKeyedServiceProvider, 			IAsyncDisposable, IServiceScopeFactory
{
    // this scope中所有實(shí)現(xiàn)IDisposable or IAsyncDisposable的服務(wù)
    private List<object>? _disposables;
    // 解析過的服務(wù)緩存(其實(shí)就是scope生命周期的服務(wù)緩存,singleton生命周期的服務(wù)緩存都直接掛在調(diào)用點(diǎn)上了)
    internal Dictionary<ServiceCacheKey, object?> ResolvedServices { get; }
    // 實(shí)錘服務(wù)商代言人
    public IServiceProvider ServiceProvider => this;
    // 沒錯(cuò)啦,通過root scope我們又能繼續(xù)創(chuàng)建無數(shù)個(gè)scope,他們彼此獨(dú)立
    public IServiceScope CreateScope() => RootProvider.CreateScope();
}

? 我們來觀察他獲取服務(wù)的邏輯,可以發(fā)現(xiàn)他就是很樸實(shí)無華的用著我們根服務(wù)商 ServiceProvider,去解析服務(wù),那 engine scope 呢,就是 this?,F(xiàn)在我們已經(jīng)隱約可以知道engine scope,就是為了滿足scope生命周期而生。而 ResolvedServices 中存的呢,就是該scope中的所有scope生命周期服務(wù)實(shí)例啦,在這個(gè)scope中他們是唯一的。

public object? GetService(Type serviceType)
{
    if (_disposed)
    {
        ThrowHelper.ThrowObjectDisposedException();
    }
    return RootProvider.GetService(ServiceIdentifier.FromServiceType(serviceType), this);
}

? 再來看另一個(gè)核心的方法:CaptureDisposable,實(shí)現(xiàn)disposable的服務(wù)會(huì)被添加到 _disposables。

internal object? CaptureDisposable(object? service)
{
    // 如果服務(wù)沒有實(shí)現(xiàn) IDisposable or IAsyncDisposable,那么不需要捕獲,直接原路返回
    if (ReferenceEquals(this, service) || !(service is IDisposable || service is IAsyncDisposable))
    {
        return service;
    }
    bool disposed = false;
    lock (Sync)
    {
        if (_disposed) // 如果scope已經(jīng)銷毀則進(jìn)入銷毀流程
        {
            disposed = true;
        }
        else
        {
            _disposables ??= new List<object>();
            _disposables.Add(service);
        }
    }
    // Don't run customer code under the lock
    if (disposed) // 這表示我們?cè)谠噲D捕獲可銷毀服務(wù)時(shí),scope就已經(jīng)被銷毀
    {
        if (service is IDisposable disposable)
        {
            disposable.Dispose();
        }
        else
        {
            // sync over async, for the rare case that an object only implements IAsyncDisposable and may end up starving the thread pool.
            object? localService = service; // copy to avoid closure on other paths
            Task.Run(() => ((IAsyncDisposable)localService).DisposeAsync().AsTask()).GetAwaiter().GetResult();
        }
        // 這種case會(huì)拋出一個(gè)ObjectDisposedException
        ThrowHelper.ThrowObjectDisposedException();
    }
    return service;
}

? 在engine scope銷毀時(shí),其作用域中所有scope生命周期且實(shí)現(xiàn)了disposable的服務(wù)(其實(shí)就是_disposable)呢,也會(huì)被一同的銷毀。

public ValueTask DisposeAsync()
{
    List<object>? toDispose = BeginDispose(); // 獲取_disposable
    if (toDispose != null)
    {
        // 從后往前,依次銷毀服務(wù)
    }
}

? 那么有同學(xué)可能就要問了:?jiǎn)卫龑?shí)例既然不存在root scope中,而是單獨(dú)丟到了調(diào)用點(diǎn)上,那他是咋銷毀的?壓根沒看到啊,那不得泄露了?

? 其實(shí)呀,同學(xué)們并不用擔(dān)心這個(gè)問題。首先,單例服務(wù)的實(shí)例確實(shí)是緩存在調(diào)用點(diǎn)上,但 disable 服務(wù)仍然會(huì)被 scope 捕獲呀(在下文會(huì)詳細(xì)介紹)。在 BeginDispose 中的,我們會(huì)去判斷,如果是 singleton case,且root scope 沒有被銷毀過,我們會(huì)主動(dòng)去銷毀喔~

if (IsRootScope && !RootProvider.IsDisposed()) RootProvider.Dispose();

1.3 ServiceCallSite

? ServiceCallSite 的主要職責(zé)是封裝服務(wù)解析的邏輯,它可以代表一個(gè)構(gòu)造函數(shù)調(diào)用、屬性注入、工廠方法調(diào)用等。DI系統(tǒng)使用這個(gè)抽象來表示服務(wù)的各種解析策略,并且可以通過它來生成服務(wù)實(shí)例。

internal abstract class ServiceCallSite
{
    protected ServiceCallSite(ResultCache cache)
	 {
	     Cache = cache;
	 }
    public abstract Type ServiceType { get; }
	 public abstract Type? ImplementationType { get; }
	 public abstract CallSiteKind Kind { get; }
	 public ResultCache Cache { get; }
	 public object? Value { get; set; }
	 public object? Key { get; set; }
	 public bool CaptureDisposable => ImplementationType == null ||
    	typeof(IDisposable).IsAssignableFrom(ImplementationType) ||
    	typeof(IAsyncDisposable).IsAssignableFrom(ImplementationType);
}

1.3.1 ResultCache

? 其中 ResultCache 定義了我們?nèi)绾尉彺娼馕龊蟮慕Y(jié)果

public CallSiteResultCacheLocation Location { get; set; } // 緩存位置
public ServiceCacheKey Key { get; set; } // 服務(wù)key(鍵化服務(wù)用的)

? CallSiteResultCacheLocation 是一個(gè)枚舉,定義了幾個(gè)值

  • Root:表示服務(wù)實(shí)例應(yīng)該在根級(jí)別的 IServiceProvider 中緩存。這通常意味著服務(wù)實(shí)例是單例的(Singleton),在整個(gè)應(yīng)用程序的生命周期內(nèi)只會(huì)創(chuàng)建一次,并且在所有請(qǐng)求中共享。
  • Scope:表示服務(wù)實(shí)例應(yīng)該在當(dāng)前作用域(Scope)中緩存。對(duì)于作用域服務(wù)(Scoped),實(shí)例會(huì)在每個(gè)作用域中創(chuàng)建一次,并在該作用域內(nèi)的所有請(qǐng)求中共享。
  • Dispose:盡管這個(gè)名稱可能會(huì)讓人誤解,但在 ResultCache 的上下文中,Dispose 表示著服務(wù)是瞬態(tài)的(每次請(qǐng)求都創(chuàng)建新實(shí)例)。
  • None:表示沒有緩存服務(wù)實(shí)例。

? ServiceCacheKey 結(jié)構(gòu)體就是包了一下服務(wù)標(biāo)識(shí)符和一個(gè)slot,用于適配多實(shí)現(xiàn)的

internal readonly struct ServiceCacheKey : IEquatable<ServiceCacheKey>
{
    public ServiceIdentifier ServiceIdentifier { get; }
    public int Slot { get; } // 那最后一個(gè)實(shí)現(xiàn)的slot是0
}

1.3.2 CallSiteFactory.GetCallSite

? 那我們來看看調(diào)用點(diǎn)是怎么創(chuàng)建的吧,其實(shí)上面已經(jīng)出現(xiàn)過一次了:

private ServiceCallSite? CreateCallSite(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
{
    if (!_stackGuard.TryEnterOnCurrentStack()) // 防止棧溢出
    {
        return _stackGuard.RunOnEmptyStack(CreateCallSite, serviceIdentifier, callSiteChain);
    }
    // 獲取服務(wù)標(biāo)識(shí)符對(duì)應(yīng)的鎖,以確保在創(chuàng)建調(diào)用點(diǎn)時(shí)的線程安全。
    // 是為了保證并行解析下的調(diào)用點(diǎn)也只會(huì)被創(chuàng)建一次,例如:
    // C -> D -> A
    // E -> D -> A
    var callsiteLock = _callSiteLocks.GetOrAdd(serviceIdentifier, static _ => new object());
    lock (callsiteLock)
    {
        // 檢查當(dāng)前服務(wù)標(biāo)識(shí)符是否會(huì)導(dǎo)致循環(huán)依賴
        callSiteChain.CheckCircularDependency(serviceIdentifier);
        // 首先嘗試創(chuàng)建精確匹配的服務(wù)調(diào)用站點(diǎn),如果失敗,則嘗試創(chuàng)建開放泛型服務(wù)調(diào)用站點(diǎn),如果還是失敗,則嘗試創(chuàng)建枚舉服務(wù)調(diào)用站點(diǎn)。如果所有嘗試都失敗了,callSite將為null。
        ServiceCallSite? callSite = TryCreateExact(serviceIdentifier, callSiteChain) ??
                                   TryCreateOpenGeneric(serviceIdentifier, callSiteChain) ??
                                   TryCreateEnumerable(serviceIdentifier, callSiteChain);
        return callSite;
    }
}

? 那服務(wù)點(diǎn)的創(chuàng)建過程我就簡(jiǎn)單概述一下啦

  • 查找調(diào)用點(diǎn)緩存,存在就直接返回啦
  • 服務(wù)標(biāo)識(shí)符會(huì)被轉(zhuǎn)成服務(wù)描述符 ServiceDescriptor (key化服務(wù)不指定key默認(rèn)取last)
  • 計(jì)算ServiceCallSite,依次是:TryCreateExact

    計(jì)算 ResultCache

    如果已經(jīng)有實(shí)現(xiàn)實(shí)例了,則返回 ConstantCallSite:表示直接返回已經(jīng)創(chuàng)建的實(shí)例的調(diào)用點(diǎn)。

    如果有實(shí)現(xiàn)工廠,則返回 FactoryCallSite:表示通過工廠方法創(chuàng)建服務(wù)實(shí)例的調(diào)用點(diǎn)。

    如果有實(shí)現(xiàn)類型,則返回 ConstructorCallSite:表示通過構(gòu)造函數(shù)創(chuàng)建服務(wù)實(shí)例的調(diào)用點(diǎn)。

    TryCreateOpenGeneric

    根據(jù)泛型定義獲取服務(wù)描述符 ServiceDescriptor

    計(jì)算 ResultCache

    使用服務(wù)標(biāo)識(shí)符中的具體泛型參數(shù)來構(gòu)造實(shí)現(xiàn)的閉合類型

    AOT兼容性測(cè)試(因?yàn)椴荒鼙WC值類型泛型的代碼已經(jīng)生成)

    如果成功閉合,則返回 ConstructorCallSite:表示通過構(gòu)造函數(shù)創(chuàng)建服務(wù)實(shí)例的調(diào)用點(diǎn)。

    TryCreateEnumerable

    確定類型是 IEnumerable<T>

    AOT兼容性測(cè)試(因?yàn)椴荒鼙WC值類型數(shù)組的代碼已經(jīng)生成)

    如果 T 不是泛型類型,并且可以找到對(duì)應(yīng)的服務(wù)描述符集合,則循環(huán) TryCreateExact

    否則,方向循環(huán) TryCreateExact,然后方向循環(huán) TryCreateOpenGeneric

1.4 CallSiteVisitor

? 好了,有了上面的了解我們可以開始探索服務(wù)解析的內(nèi)幕了。服務(wù)解析說白了就是引擎圍著 CallSiteVisitor 轉(zhuǎn)圈圈,所謂的解析服務(wù),其實(shí)就是訪問調(diào)用點(diǎn)了。

protected virtual TResult VisitCallSite(ServiceCallSite callSite, TArgument argument)
{
    if (!_stackGuard.TryEnterOnCurrentStack()) // 一些校驗(yàn),分棧啥的
    {
        return _stackGuard.RunOnEmptyStack(VisitCallSite, callSite, argument);
    }
    switch (callSite.Cache.Location)
    {
        case CallSiteResultCacheLocation.Root: // 單例
            return VisitRootCache(callSite, argument);
        case CallSiteResultCacheLocation.Scope: // 作用域
            return VisitScopeCache(callSite, argument);
        case CallSiteResultCacheLocation.Dispose: // 瞬態(tài)
            return VisitDisposeCache(callSite, argument);
        case CallSiteResultCacheLocation.None: // 不緩存(ConstantCallSite)
            return VisitNoCache(callSite, argument);
        default:
            throw new ArgumentOutOfRangeException();
    }
}

? 為了方便展示,我們這里的解析器都拿運(yùn)行時(shí)來說,因?yàn)閮?nèi)部是反射,而emit、expression實(shí)在是難以觀看。

1.4.1 VisitRootCache

? 那我們來看看單例的情況下,是如何訪問的:

protected override object? VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
{
    if (callSite.Value is object value)
    {
        // Value already calculated, return it directly
        return value;
    }
    var lockType = RuntimeResolverLock.Root;
    // 單例都是直接放根作用域的
    ServiceProviderEngineScope serviceProviderEngine = context.Scope.RootProvider.Root;
    lock (callSite)
    {
        // 這里搞了個(gè)雙檢鎖來確保在多線程環(huán)境中,同一時(shí)間只有一個(gè)線程可以執(zhí)行接下來的代碼塊。
        // Lock the callsite and check if another thread already cached the value
        if (callSite.Value is object callSiteValue)
        {
            return callSiteValue;
        }
        object? resolved = VisitCallSiteMain(callSite, new RuntimeResolverContext
        {
            Scope = serviceProviderEngine,
            AcquiredLocks = context.AcquiredLocks | lockType
        });
        // 捕獲可銷毀的服務(wù)
        serviceProviderEngine.CaptureDisposable(resolved);
        // 緩存解析結(jié)果到調(diào)用點(diǎn)上
        callSite.Value = resolved;
        return resolved;
    }
}

? 好,可以看到真正解析調(diào)用點(diǎn)的主角出來了 VisitCallSiteMain,那這里的 CallSiteKind 上面計(jì)算 ServiceCallSite 時(shí)呢已經(jīng)講的很清楚啦,咱對(duì)號(hào)入座就行了

protected virtual TResult VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
{
    switch (callSite.Kind)
    {
        case CallSiteKind.Factory:
            return VisitFactory((FactoryCallSite)callSite, argument);
        case  CallSiteKind.IEnumerable:
            return VisitIEnumerable((IEnumerableCallSite)callSite, argument);
        case CallSiteKind.Constructor:
            return VisitConstructor((ConstructorCallSite)callSite, argument);
        case CallSiteKind.Constant:
            return VisitConstant((ConstantCallSite)callSite, argument);
        case CallSiteKind.ServiceProvider:
            return VisitServiceProvider((ServiceProviderCallSite)callSite, argument);
        default:
            throw new NotSupportedException(SR.Format(SR.CallSiteTypeNotSupported, callSite.GetType()));
    }
}

? 我們就看看最經(jīng)典的通過構(gòu)造函數(shù)創(chuàng)建服務(wù)實(shí)例的調(diào)用點(diǎn) ConstructorCallSite,很顯然就是new嘛,只不過可能構(gòu)造中依賴其它服務(wù),那就遞歸創(chuàng)建唄。easy,其它幾種太簡(jiǎn)單了大家自己去看看吧。

protected override object VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
{
    object?[] parameterValues;
    if (constructorCallSite.ParameterCallSites.Length == 0)
    {
        parameterValues = Array.Empty<object>();
    }
    else
    {
        parameterValues = new object?[constructorCallSite.ParameterCallSites.Length];
        for (int index = 0; index < parameterValues.Length; index++)
        {
            // 遞歸構(gòu)建依賴的服務(wù)
            parameterValues[index] = VisitCallSite(constructorCallSite.ParameterCallSites[index], context);
        }
    }
    // new (xxx)
    return constructorCallSite.ConstructorInfo.Invoke(BindingFlags.DoNotWrapExceptions, binder: null, parameters: parameterValues, culture: null);
}

1.4.2 VisitScopeCache

? 在訪問單例緩存的時(shí)候呢,僅僅通過了一個(gè)double check lock就搞定了,因?yàn)槿思胰值穆?,咱再來看看訪問作用域緩存,會(huì)不會(huì)有什么不一樣

protected override object? VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
{
    // Check if we are in the situation where scoped service was promoted to singleton
    // and we need to lock the root
    return context.Scope.IsRootScope ?
        VisitRootCache(callSite, context) :
        VisitCache(callSite, context, context.Scope, RuntimeResolverLock.Scope);
}

? 哈哈,它果然很不一般啊,上來就來檢查我們是否是 root scope。如果是這種case呢,則走 VisitRootCache。但是奇怪啊,為什么訪問 scope cache,所在 engine scope 能是 root scope?

? 還記得 ServiceProvider 獲取的服務(wù)實(shí)例的核心方法嗎?engine scope 他是傳進(jìn)來的,如果我們給一個(gè) root scope,自然就出現(xiàn)的這種case,只是這種 case 特別罕見。

internal object? GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)

? VisitCache 的同步模型寫的實(shí)在是酷,我們看 RuntimeResolverLock 的枚舉就兩個(gè):Scope = 1 和 Root = 2

  • AcquiredLocks=Scope時(shí)

  • 那 AcquiredLocks&false==0 顯然成立,申請(qǐng)鎖,也就是嘗試獨(dú)占改作用域的ResolvedServices

  • 申請(qǐng)成功進(jìn)入同步塊,重新計(jì)算AcquiredLocks|true=1

  • 如此,在該engine scope 中這條鏈路上的調(diào)用點(diǎn)都被占有,直到結(jié)束

  • AcquiredLocks=Root 時(shí)

    • 那顯然 engine scope 也應(yīng)該是 root scope,那么走 VisitRootCache case
    • 在 VisitRootCache 通過DCL鎖住 root scope 上鏈路涉及的服務(wù)點(diǎn),直至結(jié)束

? 至此我們應(yīng)該不難看出這個(gè)設(shè)計(jì)的精妙之處,即在非 root scope(scope生命周期)中,scope之間是互相隔離的,沒有必要像 root scope(singleton生命周期)那樣,在所有scope中獨(dú)占服務(wù)點(diǎn)。

private object? VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine
{
    bool lockTaken = false;
    object sync = serviceProviderEngine.Sync;
    Dictionary<ServiceCacheKey, object?> resolvedServices = serviceProviderEngine.ResolvedServices;
    if ((context.AcquiredLocks & lockType) == 0)
    {
        Monitor.Enter(sync, ref lockTaken);
    }
    try
    {
        // Note: This method has already taken lock by the caller for resolution and access synchronization.
        // For scoped: takes a dictionary as both a resolution lock and a dictionary access lock.
        if (resolvedServices.TryGetValue(callSite.Cache.Key, out object? resolved))
        {
            return resolved;
        }
        // scope服務(wù)的解析結(jié)果是放在engine scope的ResolvedServices上的,而非調(diào)用點(diǎn)
        resolved = VisitCallSiteMain(callSite, new RuntimeResolverContext
        {
            Scope = serviceProviderEngine,
            AcquiredLocks = context.AcquiredLocks | lockType
        });
        serviceProviderEngine.CaptureDisposable(resolved);
        resolvedServices.Add(callSite.Cache.Key, resolved);
        return resolved;
    }
    finally
    {
        if (lockTaken)
        {
            Monitor.Exit(sync);
        }
    }
}

1.4.3 VisitDisposeCache

? 我們看最后一個(gè),也就是 Transient case

protected override object? VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
{
    return context.Scope.CaptureDisposable(VisitCallSiteMain(transientCallSite, context));
}

? 異常的簡(jiǎn)單,我們沿用了scope的設(shè)計(jì),但是我們沒有進(jìn)行任何緩存行為。即,每次都去訪問調(diào)用點(diǎn)。

到此這篇關(guān)于.NET8 依賴注入的文章就介紹到這了,更多相關(guān).NET8 依賴注入內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • .NET Core中反解ObjectId

    .NET Core中反解ObjectId

    這篇文章主要介紹了.NET Core中實(shí)現(xiàn)ObjectId反解的方法,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-08-08
  • ASP.NET?Core使用AutoMapper組件

    ASP.NET?Core使用AutoMapper組件

    這篇文章介紹了ASP.NET?Core使用AutoMapper組件的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-04-04
  • asp.net core 騰訊驗(yàn)證碼的接入示例代碼

    asp.net core 騰訊驗(yàn)證碼的接入示例代碼

    這篇文章主要介紹了asp.net core 騰訊驗(yàn)證碼的接入示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • .NET5實(shí)現(xiàn)操作注冊(cè)表的方法

    .NET5實(shí)現(xiàn)操作注冊(cè)表的方法

    本文詳細(xì)講解了.NET5實(shí)現(xiàn)操作注冊(cè)表的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-02-02
  • Web.Config文件配置之限制上傳文件大小和時(shí)間的屬性配置

    Web.Config文件配置之限制上傳文件大小和時(shí)間的屬性配置

    在Web.Config文件中配置限制上傳文件大小與時(shí)間字符串時(shí),是在httpRuntime httpRuntime節(jié)中完成的,需要設(shè)置以下2個(gè)屬性:maxRequestLength屬性與ExecutionTimeout屬性,感興趣的朋友可以了解下,或許對(duì)你有所幫助
    2013-02-02
  • asp.net Cookie跨域、虛擬目錄等設(shè)置方法

    asp.net Cookie跨域、虛擬目錄等設(shè)置方法

    Cookie跨域、虛擬目錄等設(shè)置方法,需要的朋友可以參考下。
    2009-11-11
  • WPF實(shí)現(xiàn)文本描邊+外發(fā)光效果的示例代碼

    WPF實(shí)現(xiàn)文本描邊+外發(fā)光效果的示例代碼

    這篇文章主要介紹了如何利用WPF實(shí)現(xiàn)文本描邊以及外發(fā)光的效果,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)有一定幫助,需要的可以參考一下
    2022-03-03
  • 詳解asp.net core封裝layui組件示例分享

    詳解asp.net core封裝layui組件示例分享

    本篇文章主要介紹了詳解asp.net core封裝layui組件示例分享,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10
  • Asp.net中通過Button打開另一個(gè)的frm

    Asp.net中通過Button打開另一個(gè)的frm

    本文通過實(shí)例代碼給大家介紹了asp.net中通過button打開另一個(gè)frm的方法,非常不錯(cuò),需要的朋友參考下吧
    2016-12-12
  • 三種方法讓Response.Redirect在新窗口打開

    三種方法讓Response.Redirect在新窗口打開

    通過設(shè)置form的target屬性同樣可以讓Response.Rederect所指向的url在新的窗口打開,下面為大家介紹三種具體的實(shí)現(xiàn)方法
    2013-10-10

最新評(píng)論