.NET Core 依賴注入改造(2)- 委托轉(zhuǎn)換

.NET Core 依賴注入改造(1)- 命名服務(wù)
.NET Core 依賴注入改造(2)- 委托轉(zhuǎn)換
.NET Core 依賴注入改造(3)- ILogger
.NET Core 依賴注入改造(4)- ActivatorUtilities
.NET Core 依賴注入改造(5)- Context

.NET Core 依賴注入改造(附1)- Autowired

一、

上一篇 寫到了命名服務(wù)
演示了使用命名服務(wù)+委托的方式來獲取服務(wù),
但總覺得還有一些不不爽,
為什么委托就必須使完全一樣的委托才能獲取服務(wù)?
為什么不能直接注入方法?

var provider = new ServiceCollection()
        .AddSingleton<Func<object, string>>(o => JsonConvert.SerializeObject(o)) 
        .BuildServiceProvdier();
//........
delegate string ToJsonString(object obj);
var toJsonStriong = provider.GetNamedService<ToJsonString>("ToJsonString");
var method = typeof(JsonConvert).GetMethod("SerializeObject", new[] { typeof(object) });
var provider = new ServiceCollection()
        .AddNamedSingleton("ToJsonString", method) //注入方法
        .BuildServiceProvdier();
//........
delegate string ToJsonString(object obj);
var toJsonStriong = provider.GetNamedService<ToJsonString>("ToJsonString");

所以我準(zhǔn)備繼續(xù)改造一下

二、

經(jīng)過上一次對源碼的探索,整理了官方DI框架大致的執(zhí)行邏輯


現(xiàn)在有2個需要解決的問題

  1. 做委托類型轉(zhuǎn)換
    這個只能在最后一步GetService時,先獲取原來注入的服務(wù),然后根據(jù)新的Type進(jìn)行類型轉(zhuǎn)換;
    這里只要替換掉原來的 ServiceProvider 就可以了;
  2. 在不知道原類型的情況下無法獲取原注入的服務(wù)(命名服務(wù)會簡單一些)
    所以我可以將所有注入的委托服務(wù)都轉(zhuǎn)換為MethodInfo類型注入,
    這個可以有兩種方式干涉
    a. 重新實(shí)現(xiàn) ServiceCollectionAdd 操作中替換
    b. 在編譯BuildServiceProvdier之前做,循環(huán)所有注入的服務(wù),并替換其中的委托
    a方案代價太大,而且在使用上也有限制,所以我選擇了b方案

最終我使用了一個新的擴(kuò)展方法BuildSupportDelegateServiceProvdier來代替BuildServiceProvdier

改造后的邏輯大概是這樣的

三、

擴(kuò)展方法 BuildSupportDelegateServiceProvdier

public static class DelegateServiceProvdierExtensions
{
    /// <summary>
    /// 構(gòu)造支持委托轉(zhuǎn)換的服務(wù)提供程序
    /// </summary>
    /// <param name="services"></param>
    /// <returns></returns>
    public static IServiceProvider BuildSupportDelegateServiceProvdier(this IServiceCollection services)
    {
        var methodServices = new List<ServiceDescriptor>();
        // 循環(huán)所有服務(wù)
        foreach (var item in services)
        {
            if (typeof(Delegate).IsAssignableFrom(item.ServiceType))
            {
                // 針對委托類型的服務(wù)做一次MethodInfo處理
                if (item.ImplementationInstance is Delegate)
                {
                    methodServices.Add(new ServiceDescriptor(typeof(MethodInfo), ((Delegate)item.ImplementationInstance).Method));
                }
                else if (item.ImplementationFactory != null)
                {
                    methodServices.Add(new ServiceDescriptor(typeof(MethodInfo), p => ((Delegate)item.ImplementationFactory(p)).Method, item.Lifetime));
                }
            }
        }
        methodServices.ForEach(services.Add); //注入MethodInfo服務(wù)
        var provider = services.BuildServiceProvider();
        return new DelegateServiceProvdier(provider); // 返回裝飾類
    }
}

DelegateServiceProvdier 裝飾類

/// <summary>
/// 可創(chuàng)建委托服務(wù)的提供程序
/// </summary>
internal class DelegateServiceProvdier : IServiceProvider
{
    readonly IServiceProvider _provider;
    readonly ILogger _logger;
    readonly ConcurrentDictionary<Type, object> _services;

    /// <summary>
    /// 構(gòu)造一個服務(wù)提供程序的代理
    /// </summary>
    /// <param name="provider">被代理的服務(wù)提供程序</param>
    public DelegateServiceProvdier(IServiceProvider provider)
    {
        _provider = provider ?? throw new ArgumentNullException(nameof(provider));
        _logger = _provider.GetService<ILoggerFactory>()?.CreateLogger<DelegateServiceProvdier>();
        _services = new ConcurrentDictionary<Type, object>(TypeComparer.Instance);
    }

    /// <summary>
    /// 獲取指定類型的服務(wù)
    /// </summary>
    /// <param name="serviceType">服務(wù)類型</param>
    /// <returns></returns>
    public object GetService(Type serviceType)
    {
        if (typeof(IServiceProvider) == serviceType)
        {
            return this;
        }
        // 從 _provider 中獲取服務(wù)
        var service = _provider.GetService(serviceType);


        if (service == null)
        {
            // 當(dāng)常規(guī)方式?jīng)]有獲取到服務(wù),且服務(wù)是委托類型時,嘗試獲取MethodInfo服務(wù),并返回最后一個簽名相同的MethodInfo并轉(zhuǎn)換為指定類型的委托
            return typeof(Delegate).IsAssignableFrom(serviceType)
                    ? _services.GetOrAdd(serviceType, x => ConvertDelegate(x, _provider.GetServices<MethodInfo>()))
                    : null;
        }

        if (service is Delegate delegateService)
        {
            // 當(dāng)獲取的服務(wù)是委托,但與要求的類型不符時,嘗試轉(zhuǎn)換委托類型
            if (serviceType is IServiceProvider tp
                && tp.GetService(typeof(Type)) is Type delegateType
                && typeof(Delegate).IsAssignableFrom(delegateType)
                && !delegateType.IsInstanceOfType(service))
            {
                return _services.GetOrAdd(serviceType, x => ConvertDelegate(delegateType, new[] { delegateService.Method }));
            }
            return service;
        }

        if (service is IEnumerable enumerable && serviceType.IsGenericType && serviceType.GetGenericArguments().Length == 1)
        {
            // 當(dāng)獲取的服務(wù)是泛型集合時
            var type = serviceType.GetGenericArguments()[0];
            if (type is IServiceProvider tp && tp.GetService(typeof(Type)) is Type delegateType)
            {
                return _services.GetOrAdd(serviceType, x => ConvertDelegates(delegateType, enumerable));
            }
            else
            {
                return _services.GetOrAdd(serviceType, x => ConvertDelegates(type, _provider.GetServices<MethodInfo>()));
            }
        }
        return service;
    }

    /// <summary>
    /// 轉(zhuǎn)換委托服務(wù)集合
    /// </summary>
    /// <param name="delegateType"></param>
    /// <param name="enumerable"></param>
    /// <returns></returns>
    private IEnumerable ConvertDelegates(Type delegateType, IEnumerable enumerable)
    {
        var newServices = new ArrayList();
        var delegateMethod = delegateType.GetMethod("Invoke");
        foreach (var item in enumerable)
        {
            if (delegateType.IsInstanceOfType(item))
            {
                newServices.Add(item);
                continue;
            }
            var method = (item as Delegate)?.Method ?? item as MethodInfo;
            if (CompareMethodSignature(delegateMethod, method))
            {
                newServices.Add(method.CreateDelegate(delegateType, null));
            }
        }
        return newServices.ToArray(delegateType);
    }

    /// <summary>
    /// 轉(zhuǎn)換委托服務(wù)
    /// </summary>
    private object ConvertDelegate(Type delegateType, IEnumerable<MethodInfo> methods)
    {
        var delegateName = delegateType.Name;
        var delegateMethod = delegateType.GetMethod("Invoke");
        MethodInfo last = null;
        MethodInfo lastExact = null;
        foreach (var method in methods)
        {
            if (CompareMethodSignature(method, delegateMethod))
            {
                if (method.Name == delegateName)
                {
                    lastExact = method;
                }
                else if (lastExact == null)
                {
                    last = method;
                }
            }
        }
        try
        {
            return (lastExact ?? last).CreateDelegate(delegateType, null);
        }
        catch (Exception ex)
        {
            _logger?.LogError(ex, ex.Message);
            return null;
        }
    }


    /// <summary>
    /// 比較2個方法簽名是否相同
    /// </summary>
    /// <param name="method1">方法1</param>
    /// <param name="method2">方法2</param>
    /// <returns></returns>
    private bool CompareMethodSignature(MethodInfo method1, MethodInfo method2)
    {
        if (method1 == null || method2 == null || method1.ReturnType != method2.ReturnType)
        {
            return false;
        }
        var p1 = method1.GetParameters();
        var p2 = method2.GetParameters();
        if (p1.Length != p2.Length)
        {
            return false;
        }
        for (var i = 0; i < p1.Length; i++)
        {
            if (p1[i].ParameterType != p2[i].ParameterType)
            {
                return false;
            }
        }
        return true;
    }
}

四、

這里有一個坑,需要單開一節(jié)來說下

new ConcurrentDictionary<Type, object>(TypeComparer.Instance);

之前代碼中的 TypeComparer.Instance 其實(shí)是一個自定義實(shí)現(xiàn)

原因是

在獲取服務(wù)集合時(provider.GetServices<ToJsonString>),的一個特殊對象TypeBuilderInstantiation

public static IEnumerable<object> GetServices(this IServiceProvider provider, Type serviceType)
{
    var genericEnumerable = typeof(IEnumerable<>).MakeGenericType(serviceType);
    return (IEnumerable<object>)provider.GetRequiredService(genericEnumerable);
}

問題就出在 typeof(IEnumerable<>).MakeGenericType(serviceType); 這個操作上

仔細(xì)看t1t2的類型是不同的,關(guān)鍵點(diǎn)在于NamedType并非系統(tǒng)RuntimeType類型的對象


這里的代碼,每次都會new一個新的TypeBuilderInstantiation,如果不重新實(shí)現(xiàn)IEqualityComparer<Type>,使用默認(rèn)方式比較,即使2個屬性完全相同的TypeBuilderInstantiation也無法得到相同的HashCode,將造成服務(wù)緩存無法正常工作

internal class TypeComparer : IEqualityComparer<Type>
{
    public static readonly TypeComparer Instance = new TypeComparer();
    private static readonly Type _runtimeType = typeof(int).GetType();
    public bool Equals(Type x, Type y)
    {
        if (x == null || y == null)
        {
            return x.Equals(y);
        }
        if ((x.GetType() != _runtimeType && x.IsGenericType) || (y.GetType() != _runtimeType && y.IsGenericType))
        {
            if (!Equals(x.GetGenericTypeDefinition(), y.GetGenericTypeDefinition()))
            {
                return false;
            }
            if (x.IsGenericTypeDefinition || y.IsGenericTypeDefinition)
            {
                return x.IsGenericTypeDefinition == y.IsGenericTypeDefinition;
            }
            var args1 = x.GetGenericArguments();
            var args2 = y.GetGenericArguments();
            if (args1.Length != args2.Length)
            {
                return false;
            }
            for (var i = 0; i < args1.Length; i++)
            {
                if (!Equals(args1[i], args2[i]))
                {
                    return false;
                }
            }
            return true;
        }
        return x.Equals(y);
    }

    public int GetHashCode(Type obj)
    {
        if (obj != null && obj.GetType() != _runtimeType && obj.IsGenericType)
        {
            var hashcode = obj.GetGenericTypeDefinition().GetHashCode();
            if (!obj.IsGenericTypeDefinition)
            {
                foreach (var item in obj.GetGenericArguments())
                {
                    hashcode ^= item.GetHashCode();
                }
            }
            return hashcode;
        }
        return obj?.GetHashCode() ?? 0;
    }
}

五、

最后項(xiàng)目結(jié)構(gòu)是這樣的



測試一下


據(jù)說,依賴注入時命名服務(wù)委托轉(zhuǎn)換更配哦。。。

delegate string ToJsonString(object obj);
delegate string ToXmlString(object obj);
static void Main(string[] args)
{
    var provider = new ServiceCollection()
                         .AddNamedSingleton<Func<object, string>>("ToJsonString", o => JsonConvert.SerializeObject(o))
                         .AddNamedSingleton<Func<object, string>>("ToXmlString", o => o.ToXml().ToString())
                        .BuildSupportDelegateServiceProvdier();

    var x = new
    {
        id = 1,
        name = "blqw"
    };
    var toJsonStriong = provider.GetNamedService<ToJsonString>("ToJsonString");
    Console.WriteLine(toJsonStriong(x));
    var toXmlString = provider.GetNamedService<ToXmlString>("ToXmlString");
    Console.WriteLine(toXmlString(x));

    Business.Operation(provider);
}

六、

github:https://github.com/blqw/blqw.DI/tree/master/src/blqw.DelegateServiceProvdier
nuget:https://www.nuget.org/packages/blqw.DI.DelegateServiceProvdier

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容