一個.NET內置依賴註入的小型強化版

来源:https://www.cnblogs.com/coredx/p/18138360
-Advertisement-
Play Games

前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...


前言

.NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。

但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服務,此時會導致無法實現某些需求。

ASP.NET Core目前推薦使用上下文池訪問EF Core上下文,但是某些功能需要直接使用上下文(例如Identity Core)。官方文檔建議使用自定義工廠通過上下文池獲取上下文。這其實是一種服務轉發(或委托),可以確保服務實例只有一個最終提供點,簡化管理。

但是當希望轉發的服務是開放泛型時就會出現問題。在實際請求服務時,無法通過自定義工廠得知請求的泛型服務的實際類型參數,也就無法實現對開放泛型類型的轉發。官方倉庫也有一個相關Issue:Dependency Injection of Open Generics via factory #41050。然而幾年過去後微軟依然沒有打算解決這個問題。鍵控服務這種完全新增的功能都做了,這個舉手之勞確一直放著,我不理解。一番研究後筆者確定可以通過簡單的改造來實現支持,因此有了本篇文章。

新書宣傳

有關新書的更多介紹歡迎查看《C#與.NET6 開發從入門到實踐》上市,作者親自來打廣告了!
image

正文

本來筆者打算使用繼承來擴展功能,但是幾經周折後發現微軟把關鍵類型設置為內部類和密封類,徹底斷了這條路。無奈只能克隆倉庫直接改代碼,好死不死這個庫是運行時倉庫的一部分,完整倉庫包含大量無關代碼,直接fork也會帶來一堆麻煩,最後只能克隆倉庫後複製需要的部分來修改。

CoreDX.Extensions.DependencyInjection.Abstractions

這是基礎抽象包,用於擴展ServiceDescriptor為後續改造提供基礎支持。

TypedImplementationFactoryServiceDescriptor

要實現能從自定義工廠獲取服務類型的功能,自定義工廠需要一個Type類型的參數來傳遞類型信息,那麼就需要ServiceDescriptor提供相應的構造方法重載。原始類型顯然不可能,好在這是個普通公共類,可以繼承,因此筆者繼承內置類並擴展了相應的成員來承載工廠委托。

/// <inheritdoc />
[DebuggerDisplay("{DebuggerToString(),nq}")]
public class TypedImplementationFactoryServiceDescriptor : ServiceDescriptor
{
    private object? _typedImplementationFactory;

    /// <summary>
    /// Gets the typed factory used for creating service instances.
    /// </summary>
    public Func<IServiceProvider, Type, object>? TypedImplementationFactory
    {
        get
        {
            if (IsKeyedService)
            {
                throw new InvalidOperationException("This service descriptor is keyed. Your service provider may not support keyed services.");
            }
            return (Func<IServiceProvider, Type, object>?)_typedImplementationFactory;
        }
    }

    private object? _typedKeyedImplementationFactory;

    /// <summary>
    /// Gets the typed keyed factory used for creating service instances.
    /// </summary>
    public Func<IServiceProvider, object?, Type, object>? TypedKeyedImplementationFactory
    {
        get
        {
            if (!IsKeyedService)
            {
                throw new InvalidOperationException("This service descriptor is not keyed.");
            }
            return (Func<IServiceProvider, object?, Type, object>?)_typedKeyedImplementationFactory;
        }
    }

    /// <summary>
    /// Don't use this!
    /// </summary>
    /// <inheritdoc />
    public TypedImplementationFactoryServiceDescriptor(
        Type serviceType,
        object instance)
        : base(serviceType, instance)
    {
        ThrowCtor();
    }

    /// <summary>
    /// Don't use this!
    /// </summary>
    /// <inheritdoc />
    public TypedImplementationFactoryServiceDescriptor(
        Type serviceType,
        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType,
        ServiceLifetime lifetime)
        : base(serviceType, implementationType, lifetime)
    {
        ThrowCtor();
    }

    /// <summary>
    /// Don't use this!
    /// </summary>
    /// <inheritdoc />
    public TypedImplementationFactoryServiceDescriptor(
        Type serviceType,
        object? serviceKey,
        object instance)
        : base(serviceType, serviceKey, instance)
    {
        ThrowCtor();
    }

    /// <summary>
    /// Don't use this!
    /// </summary>
    /// <inheritdoc />
    public TypedImplementationFactoryServiceDescriptor(
        Type serviceType,
        Func<IServiceProvider, object> factory,
        ServiceLifetime lifetime)
        : base(serviceType, factory, lifetime)
    {
        ThrowCtor();
    }

    /// <summary>
    /// Don't use this!
    /// </summary>
    /// <inheritdoc />
    public TypedImplementationFactoryServiceDescriptor(
        Type serviceType,
        object? serviceKey,
        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType,
        ServiceLifetime lifetime)
        : base(serviceType, serviceKey, implementationType, lifetime)
    {
        ThrowCtor();
    }

    /// <summary>
    /// Don't use this!
    /// </summary>
    /// <inheritdoc />
    public TypedImplementationFactoryServiceDescriptor(
        Type serviceType,
        object? serviceKey,
        Func<IServiceProvider, object?, object> factory,
        ServiceLifetime lifetime)
        : base(serviceType, serviceKey, factory, lifetime)
    {
        ThrowCtor();
    }

    /// <summary>
    /// Initializes a new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/> with the specified factory.
    /// </summary>
    /// <param name="serviceType">The <see cref="Type"/> of the service.</param>
    /// <param name="factory">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
    /// <param name="lifetime">The <see cref="ServiceLifetime"/> of the service.</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <inheritdoc />
    public TypedImplementationFactoryServiceDescriptor(
        Type serviceType,
        Func<IServiceProvider, Type, object> factory,
        ServiceLifetime lifetime)
        : base(serviceType, ThrowFactory, lifetime)
    {
        CheckOpenGeneric(serviceType);
        _typedImplementationFactory = factory ?? throw new ArgumentNullException(nameof(factory));
    }

    /// <summary>
    /// Initializes a new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/> with the specified factory.
    /// </summary>
    /// <param name="serviceType">The <see cref="Type"/> of the service.</param>
    /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
    /// <param name="factory">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
    /// <param name="lifetime">The <see cref="ServiceLifetime"/> of the service.</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <inheritdoc />
    public TypedImplementationFactoryServiceDescriptor(
        Type serviceType,
        object? serviceKey,
        Func<IServiceProvider, object?, Type, object> factory,
        ServiceLifetime lifetime)
        : base(serviceType, serviceKey, ThrowKeyedFactory, lifetime)
    {
        CheckOpenGeneric(serviceType);
        _typedKeyedImplementationFactory = factory ?? throw new ArgumentNullException(nameof(factory));
    }

    /// <summary>
    /// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
    /// with the specified service in <paramref name="implementationFactory"/> and the <see cref="ServiceLifetime.Singleton"/> lifetime.
    /// </summary>
    /// <param name="serviceType">The <see cref="Type"/> of the service.</param>
    /// <param name="implementationFactory">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
    /// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
    public static TypedImplementationFactoryServiceDescriptor Singleton(
        Type serviceType,
        Func<IServiceProvider, Type, object> implementationFactory)
    {
        return new(serviceType, implementationFactory, ServiceLifetime.Singleton);
    }

    /// <summary>
    /// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
    /// with the specified service in <paramref name="implementationFactory"/> and the <see cref="ServiceLifetime.Singleton"/> lifetime.
    /// </summary>
    /// <typeparam name="TService">The type of the service.</typeparam>
    /// <param name="implementationFactory">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
    /// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
    public static TypedImplementationFactoryServiceDescriptor Singleton<TService>(
        Func<IServiceProvider, Type, object> implementationFactory)
        where TService : class
    {
        return new(typeof(TService), implementationFactory, ServiceLifetime.Singleton);
    }

    /// <summary>
    /// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
    /// with the specified service in <paramref name="implementationType"/> and the <see cref="ServiceLifetime.Singleton"/> lifetime.
    /// </summary>
    /// <param name="serviceType">The <see cref="Type"/> of the service.</param>
    /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
    /// <param name="implementationType">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
    /// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
    public static TypedImplementationFactoryServiceDescriptor KeyedSingleton(
        Type serviceType,
        object? serviceKey,
        Func<IServiceProvider, object?, Type, object> implementationType)
    {
        return new(serviceType, serviceKey, implementationType, ServiceLifetime.Singleton);
    }

    /// <summary>
    /// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
    /// with the specified service in <paramref name="implementationType"/> and the <see cref="ServiceLifetime.Singleton"/> lifetime.
    /// </summary>
    /// <typeparam name="TService">The type of the service.</typeparam>
    /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
    /// <param name="implementationType">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
    /// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
    public static TypedImplementationFactoryServiceDescriptor KeyedSingleton<TService>(
        object? serviceKey,
        Func<IServiceProvider, object?, Type, object> implementationType)
        where TService : class
    {
        return new(typeof(TService), serviceKey, implementationType, ServiceLifetime.Singleton);
    }

    /// <summary>
    /// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
    /// with the specified service in <paramref name="implementationType"/> and the <see cref="ServiceLifetime.Scoped"/> lifetime.
    /// </summary>
    /// <param name="serviceType">The <see cref="Type"/> of the service.</param>
    /// <param name="implementationType">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
    /// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
    public static TypedImplementationFactoryServiceDescriptor Scoped(
        Type serviceType,
        Func<IServiceProvider, Type, object> implementationType)
    {
        return new(serviceType, implementationType, ServiceLifetime.Scoped);
    }

    /// <summary>
    /// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
    /// with the specified service in <paramref name="implementationFactory"/> and the <see cref="ServiceLifetime.Scoped"/> lifetime.
    /// </summary>
    /// <typeparam name="TService">The type of the service.</typeparam>
    /// <param name="implementationFactory">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
    /// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
    public static TypedImplementationFactoryServiceDescriptor Scoped<TService>(
        Func<IServiceProvider, Type, object> implementationFactory)
        where TService : class
    {
        return new(typeof(TService), implementationFactory, ServiceLifetime.Scoped);
    }

    /// <summary>
    /// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
    /// with the specified service in <paramref name="implementationType"/> and the <see cref="ServiceLifetime.Scoped"/> lifetime.
    /// </summary>
    /// <param name="serviceType">The <see cref="Type"/> of the service.</param>
    /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
    /// <param name="implementationType">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
    /// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
    public static TypedImplementationFactoryServiceDescriptor KeyedScoped(
        Type serviceType,
        object? serviceKey,
        Func<IServiceProvider, object?, Type, object> implementationType)
    {
        return new(serviceType, serviceKey, implementationType, ServiceLifetime.Scoped);
    }

    /// <summary>
    /// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
    /// with the specified service in <paramref name="implementationType"/> and the <see cref="ServiceLifetime.Scoped"/> lifetime.
    /// </summary>
    /// <typeparam name="TService">The type of the service.</typeparam>
    /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
    /// <param name="implementationType">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
    /// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
    public static TypedImplementationFactoryServiceDescriptor KeyedScoped<TService>(
        object? serviceKey,
        Func<IServiceProvider, object?, Type, object> implementationType)
        where TService : class
    {
        return new(typeof(TService), serviceKey, implementationType, ServiceLifetime.Scoped);
    }

    /// <summary>
    /// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
    /// with the specified service in <paramref name="implementationType"/> and the <see cref="ServiceLifetime.Transient"/> lifetime.
    /// </summary>
    /// <param name="serviceType">The <see cref="Type"/> of the service.</param>
    /// <param name="implementationType">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
    /// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
    public static TypedImplementationFactoryServiceDescriptor Transient(
        Type serviceType,
        Func<IServiceProvider, Type, object> implementationType)
    {
        return new(serviceType, implementationType, ServiceLifetime.Transient);
    }

    /// <summary>
    /// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
    /// with the specified service in <paramref name="implementationFactory"/> and the <see cref="ServiceLifetime.Transient"/> lifetime.
    /// </summary>
    /// <typeparam name="TService">The type of the service.</typeparam>
    /// <param name="implementationFactory">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
    /// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
    public static TypedImplementationFactoryServiceDescriptor Transient<TService>(
        Func<IServiceProvider, Type, object> implementationFactory)
        where TService : class
    {
        return new(typeof(TService), implementationFactory, ServiceLifetime.Transient);
    }

    /// <summary>
    /// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
    /// with the specified service in <paramref name="implementationType"/> and the <see cref="ServiceLifetime.Transient"/> lifetime.
    /// </summary>
    /// <param name="serviceType">The <see cref="Type"/> of the service.</param>
    /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
    /// <param name="implementationType">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
    /// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
    public static TypedImplementationFactoryServiceDescriptor KeyedTransient(
        Type serviceType,
        object? serviceKey,
        Func<IServiceProvider, object?, Type, object> implementationType)
    {
        return new(serviceType, serviceKey, implementationType, ServiceLifetime.Transient);
    }

    /// <summary>
    /// Creates an instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>
    /// with the specified service in <paramref name="implementationType"/> and the <see cref="ServiceLifetime.Transient"/> lifetime.
    /// </summary>
    /// <typeparam name="TService">The type of the service.</typeparam>
    /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
    /// <param name="implementationType">A factory used for creating service instances. Requested service type is provided as argument in parameter of factory.</param>
    /// <returns>A new instance of <see cref="TypedImplementationFactoryServiceDescriptor"/>.</returns>
    public static TypedImplementationFactoryServiceDescriptor KeyedTransient<TService>(
        object? serviceKey,
        Func<IServiceProvider, object?, Type, object> implementationType)
        where TService : class
    {
        return new(typeof(TService), serviceKey, implementationType, ServiceLifetime.Transient);
    }

    private string DebuggerToString()
    {
        string text = $"Lifetime = {Lifetime}, ServiceType = \"{ServiceType.FullName}\"";
        if (IsKeyedService)
        {
            text += $", ServiceKey = \"{ServiceKey}\"";

            return text + $", TypedKeyedImplementationFactory = {TypedKeyedImplementationFactory!.Method}";
        }
        else
        {
            return text + $", TypedImplementationFactory = {TypedImplementationFactory!.Method}";
        }
    }

    private static void ThrowCtor()
    {
        throw new NotSupportedException($"{nameof(TypedImplementationFactoryServiceDescriptor)} only use for typed factory.");
    }

    private static object ThrowFactory(IServiceProvider serviceProvider)
    {
        throw new InvalidOperationException("Please use typed factory instead.");
    }

    private static object ThrowKeyedFactory(IServiceProvider serviceProvider, object? serviceKey)
    {
        throw new InvalidOperationException("Please use typed keyed factory instead.");
    }

    private static void CheckOpenGeneric(Type serviceType)
    {
        if (!serviceType.IsGenericTypeDefinition)
            throw new InvalidOperationException($"{nameof(TypedImplementationFactoryServiceDescriptor)} only used for generic type definition(open generic type).");
    }
}

這個類很簡單,就是增加了用於保存Func<IServiceProvider, Type, object>Func<IServiceProvider, object?, Type, object>工廠委托的欄位和配套的構造方法和驗證邏輯。基類提供的所有功能均直接拋出異常,專門負責新增功能。

ImplementationFactoryServiceTypeHolder

internal sealed class ImplementationFactoryServiceTypeHolder(Type serviceType)
{
    private readonly Func<IServiceProvider, object?> _factory = sp => sp.GetService(serviceType);

    public Func<IServiceProvider, object?> Factory => _factory;
}

internal sealed class KeyedImplementationFactoryServiceTypeHolder(Type serviceType)
{
    private readonly Func<IServiceProvider, object?, object?> _factory = (sp, key) => (sp as IKeyedServiceProvider)?.GetKeyedService(serviceType, key);

    public Func<IServiceProvider, object?, object?> Factory => _factory;
}

internal sealed class OpenGenericImplementationFactoryServiceTypeHolder(Type serviceType)
{
    private readonly Func<IServiceProvider, Type, object?> _factory = serviceType.IsGenericTypeDefinition
        ? (sp, type) =>
        {
            var closed = serviceType.MakeGenericType(type.GenericTypeArguments);
            return sp.GetService(closed);
        }
        : throw new ArgumentException($"{nameof(serviceType)} is not generic type definition.");

    public Func<IServiceProvider, Type, object?> Factory => _factory;
}

internal sealed class KeyedOpenGenericImplementationFactoryServiceTypeHolder(Type serviceType)
{
    private readonly Func<IServiceProvider, object?, Type, object?> _factory = serviceType.IsGenericTypeDefinition
        ? (sp, key, type) =>
        {
            var closed = serviceType.MakeGenericType(type.GenericTypeArguments);
            return (sp as IKeyedServiceProvider)?.GetKeyedService(closed, key);
        }
        : throw new ArgumentException($"{nameof(serviceType)} is not generic type definition.");

    public Func<IServiceProvider, object?, Type, object?> Factory => _factory;
}

這個類也很簡單,只負責持有服務類型,並把新的工廠類型轉換到原始工廠類型方便集成進內置容器。並且這是內部輔助類型,對開發者是無感知的。

易用性擴展

最後就是提供擴展方法提供和內置容器相似的使用體驗。由於本次擴展的主要目的是實現開放髮型的服務轉發,因此筆者專門準備了一套用來註冊服務轉發的AddForward系列擴展方便使用。此處只列出部分預覽。

/// <summary>
/// Adds a service of the type specified in <paramref name="serviceType"/> with a factory
/// specified in <paramref name="implementationFactory"/> to the specified <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
/// <param name="serviceType">The type of the service to register.</param>
/// <param name="implementationFactory">The factory that creates the service.</param>
/// <param name="serviceLifetime">The <see cref="ServiceLifetime"/> of <paramref name="serviceType"/>.</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
public static IServiceCollection AddTypedFactory(
    this IServiceCollection services,
    Type serviceType,
    Func<IServiceProvider, Type, object> implementationFactory,
    ServiceLifetime serviceLifetime)
{
    services.Add(new TypedImplementationFactoryServiceDescriptor(serviceType, implementationFactory, serviceLifetime));
    return services;
}

/// <summary>
/// Adds a service of the type specified in <paramref name="serviceType"/> with a factory
/// specified in <paramref name="implementationFactory"/> to the specified <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
/// <param name="serviceType">The type of the service to register.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="implementationFactory">The factory that creates the service.</param>
/// <param name="serviceLifetime">The <see cref="ServiceLifetime"/> of <paramref name="serviceType"/>.</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
public static IServiceCollection AddKeyedTypedFactory(
    this IServiceCollection services,
    Type serviceType,
    object? serviceKey,
    Func<IServiceProvider, object?, Type, object> implementationFactory,
    ServiceLifetime serviceLifetime)
{
    services.Add(new TypedImplementationFactoryServiceDescriptor(serviceType, serviceKey, implementationFactory, serviceLifetime));
    return services;
}

/// <summary>
/// Adds a service of the type specified in <paramref name="serviceType"/> with a forward of the type
/// specified in <paramref name="forwardTargetServiceType"/> to the specified <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
/// <param name="serviceType">The type of the service to register.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="forwardTargetServiceType">The forward type of the service.</param>
/// <param name="serviceLifetime">The <see cref="ServiceLifetime"/> of <paramref name="serviceType"/>.</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
public static IServiceCollection AddKeyedForward(
    this IServiceCollection services,
    Type serviceType,
    object? serviceKey,
    Type forwardTargetServiceType,
    ServiceLifetime serviceLifetime)
{
    ArgumentNullException.ThrowIfNull(services);
    ArgumentNullException.ThrowIfNull(serviceType);
    ArgumentNullException.ThrowIfNull(forwardTargetServiceType);

    if (serviceType.IsGenericTypeDefinition)
    {
        services.Add(new TypedImplementationFactoryServiceDescriptor(serviceType, serviceKey, new KeyedOpenGenericImplementationFactoryServiceTypeHolder(forwardTargetServiceType).Factory!, serviceLifetime));
    }
    else
    {
        services.Add(new ServiceDescriptor(serviceType, serviceKey, new KeyedImplementationFactoryServiceTypeHolder(forwardTargetServiceType).Factory!, serviceLifetime));
    }

    return services;
}

從示例可以發現如果類型不是開放泛型,是直接使用原始類型進行註冊的。也就是說如果安裝這個抽象包,但是不使用開放泛型的相關功能,是可以直接用原始內置容器的。

CoreDX.Extensions.DependencyInjection

這是修改後的服務容器實現,增加了對帶服務類型的自定義工廠的支持,其他內置功能完全不變。

CallSiteFactory

internal sealed partial class CallSiteFactory : IServiceProviderIsService, IServiceProviderIsKeyedService
{
    // 其他原始代碼

    private void Populate()
    {
        foreach (ServiceDescriptor descriptor in _descriptors)
        {
            Type serviceType = descriptor.ServiceType;

            #region 驗證可識別請求類型的服務實現工廠

            if (descriptor is TypedImplementationFactoryServiceDescriptor typedFactoryDescriptor)
            {
                if(typedFactoryDescriptor.IsKeyedService && typedFactoryDescriptor.TypedKeyedImplementationFactory == null)
                {
                    throw new ArgumentException(
                        $"Keyed open generic service {serviceType} requires {nameof(typedFactoryDescriptor.TypedKeyedImplementationFactory)}",
                        "descriptors");
                }
                else if (!typedFactoryDescriptor.IsKeyedService && typedFactoryDescriptor.TypedImplementationFactory == null)
                {
                    throw new ArgumentException(
                        $"Open generic service {serviceType} requires {nameof(typedFactoryDescriptor.TypedImplementationFactory)}",
                        "descriptors");
                }
            }

            #endregion

            // 其他原始代碼
        }
    }

    private ServiceCallSite? TryCreateExact(ServiceDescriptor descriptor, ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain, int slot)
    {
        if (serviceIdentifier.ServiceType == descriptor.ServiceType)
        {
            ServiceCacheKey callSiteKey = new ServiceCacheKey(serviceIdentifier, slot);
            if (_callSiteCache.TryGetValue(callSiteKey, out ServiceCallSite? serviceCallSite))
            {
                return serviceCallSite;
            }

            ServiceCallSite callSite;
            var lifetime = new ResultCache(descriptor.Lifetime, serviceIdentifier, slot);

            // 其他原始代碼

            #region 為可識別請求類型的服務工廠註冊服務實現工廠

            else if (TryCreateTypedFactoryCallSite(lifetime, descriptor as TypedImplementationFactoryServiceDescriptor, descriptor.ServiceType) is ServiceCallSite factoryCallSite)
            {
                callSite = factoryCallSite;
            }

            #endregion

            // 其他原始代碼

            return _callSiteCache[callSiteKey] = callSite;
        }

        return null;
    }

    [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2055:MakeGenericType",
        Justification = "MakeGenericType here is used to create a closed generic implementation type given the closed service type. " +
        "Trimming annotations on the generic types are verified when 'Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability' is set, which is set by default when PublishTrimmed=true. " +
        "That check informs developers when these generic types don't have compatible trimming annotations.")]
    [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode",
        Justification = "When ServiceProvider.VerifyAotCompatibility is true, which it is by default when PublishAot=true, " +
        "this method ensures the generic types being created aren't using ValueTypes.")]
    private ServiceCallSite? TryCreateOpenGeneric(ServiceDescriptor descriptor, ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain, int slot, bool throwOnConstraintViolation)
    {
        if (serviceIdentifier.IsConstructedGenericType &&
            serviceIdentifier.ServiceType.GetGenericTypeDefinition() == descriptor.ServiceType)
        {
            ServiceCacheKey callSiteKey = new ServiceCacheKey(serviceIdentifier, slot);
            if (_callSiteCache.TryGetValue(callSiteKey, out ServiceCallSite? serviceCallSite))
            {
                return serviceCallSite;
            }

            Type? implementationType = descriptor.GetImplementationType();
            //Debug.Assert(implementationType != null, "descriptor.ImplementationType != null"); // 延遲斷言,此處可能是開放泛型工廠
            var lifetime = new ResultCache(descriptor.Lifetime, serviceIdentifier, slot);
            Type closedType;
            try
            {
                Type[] genericTypeArguments = serviceIdentifier.ServiceType.GenericTypeArguments;
                if (TypedImplementationFactoryServiceProvider.VerifyAotCompatibility)
                {
                    VerifyOpenGenericAotCompatibility(serviceIdentifier.ServiceType, genericTypeArguments);
                }

                #region 為開放式泛型服務添加可識別請求類型的服務實現工廠

                if (descriptor is TypedImplementationFactoryServiceDescriptor typedFactoryDescriptor)
                {
                    closedType = typedFactoryDescriptor.ServiceType.MakeGenericType(genericTypeArguments);
                    if (TryCreateTypedFactoryCallSite(lifetime, typedFactoryDescriptor, closedType) is ServiceCallSite factoryCallSite)
                    {
                        return _callSiteCache[callSiteKey] = factoryCallSite;
                    }
                    else
                    {
                        return null;
                    }
                }

                // 斷言移動到此處
                Debug.Assert(implementationType != null, "descriptor.ImplementationType != null");

                #endregion

                closedType = implementationType.MakeGenericType(genericTypeArguments);
            }
            catch (ArgumentException)
            {
                if (throwOnConstraintViolation)
                {
                    throw;
                }

                return null;
            }

            return _callSiteCache[callSiteKey] = CreateConstructorCallSite(lifetime, serviceIdentifier, closedType, callSiteChain);
        }

        return null;
    }

    // 其他原始代碼
}

這是整個改造的關鍵,理論上來說只要這個類是普通類的話完全可以直接通過繼承把功能加上,可惜不是。此處只展示修改的部分。然後把輔助方法定義到另一個文件,方便利用部分類的特點儘量減少對原始代碼的改動,方便將來同步官方代碼。

internal sealed partial class CallSiteFactory
{
	/// <summary>
	/// 嘗試創建可識別請求類型的工廠調用點
	/// </summary>
	/// <param name="lifetime"></param>
	/// <param name="descriptor"></param>
	/// <param name="serviceType">服務類型</param>
	/// <returns></returns>
	private static FactoryCallSite? TryCreateTypedFactoryCallSite(
		ResultCache lifetime,
		TypedImplementationFactoryServiceDescriptor? descriptor,
		Type serviceType)
	{
        ArgumentNullException.ThrowIfNull(serviceType);

        if (descriptor == null) { }
		else if (descriptor.IsKeyedService && descriptor.TypedKeyedImplementationFactory != null)
		{
			return new FactoryCallSite(lifetime, descriptor.ServiceType, descriptor.ServiceKey!, new TypedKeyedServiceImplementationFactoryHolder(descriptor.TypedKeyedImplementationFactory!, serviceType).Factory);
		}
		else if (!descriptor.IsKeyedService && descriptor.TypedImplementationFactory != null)
        {
			return new FactoryCallSite(lifetime, descriptor.ServiceType, new TypedServiceImplementationFactoryHolder(descriptor.TypedImplementationFactory!, serviceType).Factory);
		}

        return null;
	}
}

TypedServiceImplementationFactoryHolder

internal sealed class TypedServiceImplementationFactoryHolder
{
    private readonly Func<IServiceProvider, Type, object> _factory;
    private readonly Type _serviceType;

    internal TypedServiceImplementationFactoryHolder(Func<IServiceProvider, Type, object> factory, Type serviceType)
    {
        _factory = factory ?? throw new ArgumentNullException(nameof(factory));
        _serviceType = serviceType ?? throw new ArgumentNullException(nameof(serviceType));
    }

    internal Func<IServiceProvider, object> Factory => FactoryFunc;

    private object FactoryFunc(IServiceProvider provider)
    {
        return _factory(provider, _serviceType);
    }
}

internal sealed class TypedKeyedServiceImplementationFactoryHolder
{
    private readonly Func<IServiceProvider, object?, Type, object> _factory;
    private readonly Type _serviceType;

    internal TypedKeyedServiceImplementationFactoryHolder(Func<IServiceProvider, object?, Type, object> factory, Type serviceType)
    {
        _factory = factory ?? throw new ArgumentNullException(nameof(factory));
        _serviceType = serviceType ?? throw new ArgumentNullException(nameof(serviceType));
    }

    internal Func<IServiceProvider, object?, object> Factory => FactoryFunc;

    private object FactoryFunc(IServiceProvider provider, object? serviceKey)
    {
        return _factory(provider, serviceKey, _serviceType);
    }
}

因為筆者直接使用了內置類型,因此需要把工廠委托轉換成內置容器支持的簽名。Holder輔助類就可以把類型信息保存為內部欄位,對外暴露的工廠簽名就可以不需要類型參數了。

最後為避免引起誤解,筆者修改了類名,但保留文件名方便比對原始倉庫代碼。至此,改造其實已經完成。可以看出改動真的很少,不知道為什麼微軟就是不改。

CoreDX.Extensions.DependencyInjection.Hosting.Abstractions

雖然經過上面的改造後,改版容器已經能用了,但是為了方便和通用主機系統集成還是要提供一個替換容器用的擴展。

TypedImplementationFactoryHostingHostBuilderExtensions

public static class TypedImplementationFactoryHostingHostBuilderExtensions
{
    /// <summary>
    /// Specify the <see cref="IServiceProvider"/> to be the typed implementation factory supported one.
    /// </summary>
    /// <param name="hostBuilder">The <see cref="IHostBuilder"/> to configure.</param>
    /// <returns>The <see cref="IHostBuilder"/>.</returns>
    public static IHostBuilder UseTypedImplementationFactoryServiceProvider(
        this IHostBuilder hostBuilder)
        => hostBuilder.UseTypedImplementationFactoryServiceProvider(static _ => { });

    /// <summary>
    /// Specify the <see cref="IServiceProvider"/> to be the typed implementation factory supported one.
    /// </summary>
    /// <param name="hostBuilder">The <see cref="IHostBuilder"/> to configure.</param>
    /// <param name="configure">The delegate that configures the <see cref="IServiceProvider"/>.</param>
    /// <returns>The <see cref="IHostBuilder"/>.</returns>
    public static IHostBuilder UseTypedImplementationFactoryServiceProvider(
        this IHostBuilder hostBuilder,
        Action<ServiceProviderOptions> configure)
        => hostBuilder.UseTypedImplementationFactoryServiceProvider((context, options) => configure(options));

    /// <summary>
    /// Specify the <see cref="IServiceProvider"/> to be the typed implementation factory supported one.
    /// </summary>
    /// <param name="hostBuilder">The <see cref="IHostBuilder"/> to configure.</param>
    /// <param name="configure">The delegate that configures the <see cref="IServiceProvider"/>.</param>
    /// <returns>The <see cref="IHostBuilder"/>.</returns>
    public static IHostBuilder UseTypedImplementationFactoryServiceProvider(
        this IHostBuilder hostBuilder,
        Action<HostBuilderContext, ServiceProviderOptions> configure)
    {
        return hostBuilder.UseServiceProviderFactory(context =>
        {
            var options = new ServiceProviderOptions();
            configure(context, options);
            return new TypedImplementationFactoryServiceProviderFactory(options);
        });
    }
}

至此,主機集成工作也完成了。本來打算就這麼結束的,結果突然想起來,開放泛型問題解決了,鍵控服務也有了,之前一直不知道怎麼辦的動態代理貌似是有戲了,就又研究起來了。

CoreDX.Extensions.DependencyInjection.Proxies.Abstractions

之前動態代理不好實現主要是因為代理服務和原始服務的註冊類型相同,實在是沒辦法。既然現在有鍵控服務了,那麼把原始服務和代理服務用鍵分開就完美搞定,最後一個問題就是鍵要怎麼處理。通過文檔可知鍵控服務的鍵可以是任意object,只要實現合理的相等性判斷即可。因此筆者決定使用專用的類型來表示代理服務的鍵,並通過對string類型的特殊處理來實現特性鍵指定的相容。

ImplicitProxyServiceOriginalServiceKey

/// <summary>
/// Service key for access original service that already added as implicit proxy.
/// </summary>
public sealed class ImplicitProxyServiceOriginalServiceKey
    : IEquatable<ImplicitProxyServiceOriginalServiceKey>
#if NET7_0_OR_GREATER
    , IEqualityOperators<ImplicitProxyServiceOriginalServiceKey, ImplicitProxyServiceOriginalServiceKey, bool>
    , IEqualityOperators<ImplicitProxyServiceOriginalServiceKey, object, bool>
#endif
{
    private const int _hashCodeBase = 870983858;

    private readonly bool _isStringMode;
    private readonly object? _originalServiceKey;

    private static readonly ImplicitProxyServiceOriginalServiceKey _default = CreateOriginalServiceKey(null);
    private static readonly ImplicitProxyServiceOriginalServiceKey _stringDefault = CreateStringOriginalServiceKey(null);

    /// <summary>
    /// Prefix for access original <see cref="string"/> based keyed service that already added as implicit proxy.
    /// </summary>
    public const string DefaultStringPrefix = $"[{nameof(CoreDX)}.{nameof(Extensions)}.{nameof(DependencyInjection)}.{nameof(Proxies)}.{nameof(ImplicitProxyServiceOriginalServiceKey)}](ImplicitDefault)";

    /// <summary>
    /// Default original service key for none keyed proxy service.
    /// </summary>
    public static ImplicitProxyServiceOriginalServiceKey Default => _default;

    /// <summary>
    /// Default original service key for none <see cref="string"/> based keyed proxy service.
    /// </summary>
    public static ImplicitProxyServiceOriginalServiceKey StringDefault => _stringDefault;

    /// <summary>
    /// Service key of original service.
    /// </summary>
    public object? OriginalServiceKey => _originalServiceKey;

    public bool Equals(ImplicitProxyServiceOriginalServiceKey? other)
    {
        return Equals((object?)other);
    }

    public override bool Equals(object? obj)
    {
        if (_isStringMode && obj is string str) return $"{DefaultStringPrefix}{_originalServiceKey}" == str;
        else
        {
            var isEquals = obj is not null and ImplicitProxyServiceOriginalServiceKey other
            && ((_originalServiceKey is null && other._originalServiceKey is null) || _originalServiceKey?.Equals(other._originalServiceKey) is true);

            return isEquals;
        }
    }

    public static bool operator ==(ImplicitProxyServiceOriginalServiceKey? left, ImplicitProxyServiceOriginalServiceKey? right)
    {
        return left?.Equals(right) is true;
    }

    public static bool operator !=(ImplicitProxyServiceOriginalServiceKey? left, ImplicitProxyServiceOriginalServiceKey? right)
    {
        return !(left == right);
    }

    public static bool operator ==(ImplicitProxyServiceOriginalServiceKey? left, object? right)
    {
        return left?.Equals(right) is true;
    }

    public static bool operator !=(ImplicitProxyServiceOriginalServiceKey? left, object? right)
    {
        return !(left == right);
    }

    public static bool operator ==(object? left, ImplicitProxyServiceOriginalServiceKey? right)
    {
        return right == left;
    }

    public static bool operator !=(object? left, ImplicitProxyServiceOriginalServiceKey? right)
    {
        return right != left;
    }

    public override int GetHashCode()
    {
        return _isStringMode
            ? $"{DefaultStringPrefix}{_originalServiceKey}".GetHashCode()
            : HashCode.Combine(_hashCodeBase, _originalServiceKey);
    }

    /// <summary>
    /// Creates an instance of <see cref="ImplicitProxyServiceOriginalServiceKey"/> with the specified service key in <paramref name="originalServiceKey"/>.
    /// </summary>
    /// <param name="originalServiceKey"></param>
    /// <returns>A new instance of <see cref="ImplicitProxyServiceOriginalServiceKey"/>.</returns>
    public static ImplicitProxyServiceOriginalServiceKey CreateOriginalServiceKey(object? originalServiceKey)
    {
        return new(originalServiceKey, false);
    }

    /// <summary>
    /// Creates an instance of <see cref="ImplicitProxyServiceOriginalServiceKey"/> with the specified <see cref="string"/> based service key in <paramref name="originalServiceKey"/>.
    /// </summary>
    /// <param name="originalServiceKey"></param>
    /// <returns>A new instance of <see cref="ImplicitProxyServiceOriginalServiceKey"/>.</returns>
    public static ImplicitProxyServiceOriginalServiceKey CreateStringOriginalServiceKey(string? originalServiceKey)
    {
        return new(originalServiceKey, true);
    }

    private ImplicitProxyServiceOriginalServiceKey(object? originalServiceKey, bool isStringMode)
    {
        _originalServiceKey = originalServiceKey;
        _isStringMode = isStringMode;
    }
}

對.NET 7以上版本,把運算符實現為介面。

ProxyService

/// <summary>
/// The interface for get explicit proxy service. 
/// </summary>
/// <typeparam name="TService">The type of original service to get explicit proxy.</typeparam>
public interface IProxyService<out TService>
    where TService : class
{
    /// <summary>
    /// Get proxy service instance of type <typeparamref name="TService"/>.
    /// </summary>
    TService Proxy { get; }
}

/// <summary>
/// The type for get explicit proxy service. 
/// </summary>
/// <typeparam name="TService">The type of original service to get explicit proxy.</typeparam>
/// <param name="service">Object instance of original service to be proxy.</param>
internal sealed class ProxyService<TService>(TService service) : IProxyService<TService>
    where TService : class
{
    public TService Proxy { get; } = service;
}

除了隱式代理,筆者還準備了顯式代理,這也是筆者要在內置容器上擴展而不是去用其他第三方容器的一個原因。第三方容器代理後原始服務就被隱藏了,在某些極端情況下萬一要用到原始服務就沒辦法了。

CastleDynamicProxyDependencyInjectionExtensions

此處只展示部分預覽。

/// <summary>
/// Adds a explicit proxy for the type specified in <paramref name="serviceType"/> with interceptors
/// specified in <paramref name="interceptorTypes"/> to the specified <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the service proxy to.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="serviceType">The type of the service to add proxy.</param>
/// <param name="serviceLifetime">The <see cref="ServiceLifetime"/> of <paramref name="serviceType"/> and <paramref name="interceptorTypes"/>.</param>
/// <param name="interceptorTypes">The interceptor types of the service proxy.</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
/// <remarks>Use <see cref="IProxyService{TService}"/> to get proxy service.</remarks>
public static IServiceCollection AddKeyedExplicitProxy(
    this IServiceCollection services,
    Type serviceType,
    object? serviceKey,
    ServiceLifetime serviceLifetime,
    params Type[] interceptorTypes)
{
    ArgumentNullException.ThrowIfNull(services);
    ArgumentNullException.ThrowIfNull(serviceType);
    CheckInterface(serviceType);
    CheckInterceptor(interceptorTypes);

    if (serviceType.IsGenericTypeDefinition)
    {
        services.TryAddKeyedSingleton<IStartupOpenGenericServiceProxyRegister>(serviceKey, new StartupOpenGenericServiceProxyRegister());

        var startupOpenGenericServiceProxyRegister = services
            .LastOrDefault(service => service.IsKeyedService && service.ServiceKey == serviceKey && service.ServiceType == typeof(IStartupOpenGenericServiceProxyRegister))
            ?.KeyedImplementationInstance as IStartupOpenGenericServiceProxyRegister
            ?? throw new InvalidOperationException($"Can not found keyed(key value: {serviceKey}) service of type {nameof(IStartupOpenGenericServiceProxyRegister)}");

        startupOpenGenericServiceProxyRegister?.Add(serviceType);

        services.TryAdd(new TypedImplementationFactoryServiceDescriptor(
            typeof(IProxyService<>),
            serviceKey,
            (provider, serviceKey, requestedServiceType) =>
            {
                var proxyServiceType = requestedServiceType.GenericTypeArguments[0];

                var registered = CheckKeyedOpenGenericServiceProxyRegister(provider, serviceKey, proxyServiceType.GetGenericTypeDefinition());
                if (!registered) return null!;

                var proxy = CreateKeyedProxyObject(provider, proxyServiceType, serviceKey, interceptorTypes);

                return Activator.CreateInstance(typeof(ProxyService<>).MakeGenericType(proxy.GetType()), proxy)!;
            },
            serviceLifetime));
    }
    else
    {
        services.Add(new ServiceDescriptor(
            typeof(IProxyService<>).MakeGenericType(serviceType),
            serviceKey,
            (provider, serviceKey) =>
            {
                var proxy = CreateKeyedProxyObject(provider, serviceType, serviceKey, interceptorTypes);
                return Activator.CreateInstance(typeof(ProxyService<>).MakeGenericType(proxy.GetType()), proxy)!;
            },
            serviceLifetime));
    }

    services.TryAddKeyedInterceptors(serviceKey, serviceLifetime, interceptorTypes);

    return services;
}

/// <summary>
/// Adds a implicit proxy for the type specified in <paramref name="serviceType"/> with interceptors
/// specified in <paramref name="interceptorTypes"/> to the specified <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the service proxy to.</param>
/// <param name="serviceType">The type of the service to add proxy.</param>
/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="serviceLifetime">The <see cref="ServiceLifetime"/> of <paramref name="serviceType"/> and <paramref name="interceptorTypes"/>.</param>
/// <param name="interceptorTypes">The interceptor types of the service proxy.</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
/// <remarks>
/// Use key <see cref="ImplicitProxyServiceOriginalServiceKey.CreateOriginalServiceKey(object?)"/>
/// or <see cref="ImplicitProxyServiceOriginalServiceKey.CreateStringOriginalServiceKey(string?)"/> if <paramref name="serviceKey"/> is <see cref="string"/>
/// or <see cref="ImplicitProxyServiceOriginalServiceKey.DefaultStringPrefix"/> + <paramref name="serviceKey"/> if <paramref name="serviceKey"/>
/// is <see cref="string"/>(eg. Constant value for <see cref="FromKeyedServicesAttribute"/>.) to get original service.
/// </remarks>
public static IServiceCollection AddKeyedImplicitProxy(
    this IServiceCollection services,
    Type serviceType,
    object? serviceKey,
    ServiceLifetime serviceLifetime,
    params Type[] interceptorTypes)
{
    ArgumentNullException.ThrowIfNull(services);
    ArgumentNullException.ThrowIfNull(serviceType);
    CheckInterface(serviceType);
    CheckInterceptor(interceptorTypes);

    var originalServiceDescriptor = services.LastOrDefault(service => service.IsKeyedService && service.ServiceKey == serviceKey && service.ServiceType == serviceType && service.Lifetime == serviceLifetime)
        ?? throw new ArgumentException($"Not found registered keyed(key value: {serviceKey}) \"{Enum.GetName(serviceLifetime)}\" service of type {serviceType.Name}.", nameof(serviceType));

    var newServiceKey = CreateOriginalServiceKey(serviceKey);
    var serviceDescriptorIndex = services.IndexOf(originalServiceDescriptor);
    if (originalServiceDescriptor is TypedImplementationFactoryServiceDescriptor typedServiceDescriptor)
    {
        services.Insert(
            serviceDescriptorIndex,
            new TypedImplementationFactoryServiceDescriptor(
                typedServiceDescriptor.ServiceType,
                newServiceKey,
                (serviceProvider, serviceKey, requestedServiceType) =>
                {
                    Debug.Assert(serviceKey is ImplicitProxyServiceOriginalServiceKey, $"Implicit proxy not use {nameof(ImplicitProxyServiceOriginalServiceKey)}");

                    return typedServiceDescriptor.TypedKeyedImplementationFactory!(
                        serviceProvider,
                        (serviceKey as ImplicitProxyServiceOriginalServiceKey)?.OriginalServiceKey ?? serviceKey,
                        requestedServiceType);
                },
                originalServiceDescriptor.Lifetime)
            );
    }
    else if (originalServiceDescriptor.KeyedImplementationInstance is not null)
    {
        services.Insert(
            serviceDescriptorIndex,
            new ServiceDescriptor(
                originalServiceDescriptor.ServiceType,
                newServiceKey,
                originalServiceDescriptor.KeyedImplementationInstance)
            );
    }
    else if (originalServiceDescriptor.KeyedImplementationType is not null)
    {
        services.Insert(
            serviceDescriptorIndex,
            new ServiceDescriptor(
                originalServiceDescriptor.ServiceType,
                newServiceKey,
                originalServiceDescriptor.KeyedImplementationType,
                originalServiceDescriptor.Lifetime)
            );
    }
    else if (originalServiceDescriptor.KeyedImplementationFactory is not null)
    {
        services.Insert(
            serviceDescriptorIndex,
            new ServiceDescriptor(
                originalServiceDescriptor.ServiceType,
                newServiceKey,
                (serviceProvider, serviceKey) =>
                {
                    return originalServiceDescriptor.KeyedImplementationFactory(
                        serviceProvider,
                        serviceKey);
                },
                originalServiceDescriptor.Lifetime)
            );
    }
    else throw new Exception("Add proxy service fail.");

    if (serviceType.IsGenericTypeDefinition)
    {
        services.Add(new TypedImplementationFactoryServiceDescriptor(
            serviceType,
            serviceKey,
            (provider, serviceKey, requestedServiceType) =>
            {
                var newLocalServiceKey = CreateOriginalServiceKey(serviceKey);
                var proxy = CreateKeyedProxyObject(provider, requestedServiceType, newLocalServiceKey, interceptorTypes);

                return proxy;
            },
            serviceLifetime));
    }
    else
    {
        services.Add(new ServiceDescriptor(
            serviceType,
            serviceKey,
            (provider, serviceKey) =>
            {
                var newLocalServiceKey = CreateOriginalServiceKey(serviceKey);
                var proxy = CreateKeyedProxyObject(provider, serviceType, newLocalServiceKey, interceptorTypes);

                return proxy;
            },
            serviceLifetime));
    }

    services.TryAddKeyedInterceptors(newServiceKey, serviceLifetime, interceptorTypes);

    services.Remove(originalServiceDescriptor);

    return services;
}

    /// <summary>
    /// Solidify open generic service proxy register for the specified <see cref="IServiceCollection"/>.
    /// </summary>
    /// <param name="containerBuilder">The <see cref="IServiceCollection"/> to solidify register.</param>
    /// <remarks>Should call after last add proxy. If used for host, needn't call.</remarks>
    public static void SolidifyOpenGenericServiceProxyRegister(this IServiceCollection containerBuilder)
    {
        var openGenericServiceProxyRegisters = containerBuilder
            .Where(service => service.ServiceType == typeof(IStartupOpenGenericServiceProxyRegister))
            .ToList();

        var readOnlyOpenGenericServiceProxyRegisters = openGenericServiceProxyRegisters
            .Where(service => service.Lifetime == ServiceLifetime.Singleton)
            .Select(service =>
            {
                return service.IsKeyedService switch
                {
                    true => ServiceDescriptor.KeyedSingleton<IOpenGenericServiceProxyRegister>(service.ServiceKey, new OpenGenericServiceProxyRegister((service.KeyedImplementationInstance! as IStartupOpenGenericServiceProxyRegister)!)),
                    false => ServiceDescriptor.Singleton<IOpenGenericServiceProxyRegister>(new OpenGenericServiceProxyRegister((service.ImplementationInstance! as IStartupOpenGenericServiceProxyRegister)!)),
                };
            });

        foreach (var register in openGenericServiceProxyRegisters)
        {
            containerBuilder.Remove(register);
        }

        foreach (var readOnlyRegister in readOnlyOpenGenericServiceProxyRegisters)
        {
            containerBuilder.Add(readOnlyRegister);
        }
    }

    private static object CreateProxyObject(
        IServiceProvider provider,
        Type serviceType,
        Type[] interceptorTypes)
    {
        var target = provider.GetRequiredService(serviceType);
        var interceptors = interceptorTypes.Select(t => GetInterceptor(provider.GetRequiredService(t))).ToArray();
        var proxyGenerator = provider.GetRequiredService<IProxyGenerator>();

        var proxy = proxyGenerator.CreateInterfaceProxyWithTarget(serviceType, target, interceptors);
        return proxy;
    }

    private static object CreateKeyedProxyObject(
        IServiceProvider provider,
        Type serviceType,
        object? serviceKey,
        Type[] interceptorTypes)
    {
        var target = provider.GetRequiredKeyedService(serviceType, serviceKey);
        var interceptors = interceptorTypes.Select(t => GetInterceptor(provider.GetRequiredKeyedService(t, serviceKey))).ToArray();
        var proxyGenerator = provider.GetRequiredService<IProxyGenerator>();

        var proxy = proxyGenerator.CreateInterfaceProxyWithTarget(serviceType, target, interceptors);
        return proxy;
    }

    private static ImplicitProxyServiceOriginalServiceKey CreateOriginalServiceKey(object? serviceKey)
    {
        return serviceKey switch
        {
            string stringKey => ImplicitProxyServiceOriginalServiceKey.CreateStringOriginalServiceKey(stringKey),
            _ => ImplicitProxyServiceOriginalServiceKey.CreateOriginalServiceKey(serviceKey)
        };
    }

    private static void TryAddInterceptors(
        this IServiceCollection services,
        ServiceLifetime lifetime,
        params Type[] interceptorTypes)
    {
        services.TryAddSingleton<IProxyGenerator, ProxyGenerator>();

        foreach (var interceptorType in interceptorTypes)
        {
            services.TryAdd(new ServiceDescriptor(interceptorType, interceptorType, lifetime));
        }
    }

    private static void TryAddKeyedInterceptors(
        this IServiceCollection services,
        object? serviceKey,
        ServiceLifetime lifetime,
        params Type[] interceptorTypes)
    {
        services.TryAddSingleton<IProxyGenerator, ProxyGenerator>();

        foreach (var interceptorType in interceptorTypes)
        {
            services.TryAdd(new ServiceDescriptor(interceptorType, serviceKey, interceptorType, lifetime));
        }
    }

    private static IInterceptor GetInterceptor(object interceptor)
    {
        return (interceptor as IInterceptor)
            ?? (interceptor as IAsyncInterceptor)?.ToInterceptor()
            ?? throw new InvalidCastException($"{nameof(interceptor)} is not {nameof(IInterceptor)} or {nameof(IAsyncInterceptor)}.");       
    }

    private static void CheckInterface(Type serviceType)
    {
        if (!serviceType.IsInterface)
            throw new InvalidOperationException($"Proxy need interface but {nameof(serviceType)} is not interface.");
    }

    private static void CheckInterceptor(params Type[] types)
    {
        foreach (var type in types)
        {
            if (!(type.IsAssignableTo(typeof(IInterceptor)) || type.IsAssignableTo(typeof(IAsyncInterceptor))))
                throw new ArgumentException($"Exist element in {nameof(types)} is not {nameof(IInterceptor)} or {nameof(IAsyncInterceptor)}.", $"{nameof(types)}");
        }
    }

    private static bool CheckOpenGenericServiceProxyRegister(IServiceProvider serviceProvider, Type serviceType)
    {
        var register = serviceProvider.GetService<IOpe

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 在Web開發中,文件上傳是一個常見的功能需求。Spring框架提供了MultipartFile介面,用於處理文件上傳請求。MultipartFile可以代表一個多部分文件上傳請求中的一個文件,提供了一系列方法用於獲取文件的各種屬性和內容,使得在後端處理文件上傳變得十分方便。下麵我們將介紹Multip ...
  • 本文將重點比較Bokeh和Altair這兩個常用的Python數據可視化庫,探討它們的優缺點以及在不同場景下的適用性。 ...
  • 隨著汽車的普及,車輛信息查詢變得越來越重要。無論是買車、賣車還是維修保養,瞭解車輛的詳細信息是必不可少的。而如何高效快捷地查詢車輛信息成為了很多車主的需求。幸運的是,我們有一個非常實用的介面可以滿足這個需求,而這就是挖數據平臺提供的車輛信息查詢介面。 這個介面的主要功能是通過車架號vin來查詢車輛的 ...
  • 左手編程,右手年華。大家好,我是一點,關註我,帶你走入編程的世界。 公眾號:一點sir,關註領取編程資料 介紹 函數跳轉是要給IDE中非常重要也非常常用的功能,而原生的 Vim 並不提供這個功能,這個確定有點讓人遺憾,按理說這麼常用的功能應該是要提供的。但是沒有關係,有插件可以實現這樣的功能更,藉助 ...
  • 地球人皆知,許多物聯網教程作者的心中都深愛著一燈大師,所以第一個常式總喜歡點燈,高級一點的會來個“一閃一閃亮晶晶”。老周今天要扯的也是和燈有關的,但不單純地點個燈,那樣實在不好玩,缺乏樂趣。老周打算舞個龍燈,哦不,是用 LED 彩色燈帶給伙伴們整點炫酷樂子。 說到這LED彩燈,咱們常見到的有兩類: ...
  • 民爆生產廠區有地面站和民爆車,現場地面站的控制系統為西門子PLC和歐姆龍PLC,民爆車為三菱PLC,地面站通過光纖與本地機房進行數據交互,民爆車的位置及其他數據通過4G與本地機房進行數據交互。本地機房與北京運維中心進行數據交互,實現民爆行業的綜合運維平臺。 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...