.NET

.NET Core 依赖注入改造(2)- 委托转换

2018-08-07  本文已影响108人  冰麟轻武

.NET Core 依赖注入改造(1)- 命名服务
.NET Core 依赖注入改造(2)- 委托转换
.NET Core 依赖注入改造(3)- ILogger
.NET Core 依赖注入改造(4)- ActivatorUtilities
.NET Core 依赖注入改造(5)- Context

.NET Core 依赖注入改造(附1)- Autowrite

一、

上一篇 写到了命名服务
演示了使用命名服务+委托的方式来获取服务,
但总觉得还有一些不不爽,
为什么委托就必须使完全一样的委托才能获取服务?
为什么不能直接注入方法?

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");

所以我准备继续改造一下

二、

经过上一次对源码的探索,整理了官方DI框架大致的执行逻辑


现在有2个需要解决的问题

  1. 做委托类型转换
    这个只能在最后一步GetService时,先获取原来注入的服务,然后根据新的Type进行类型转换;
    这里只要替换掉原来的 ServiceProvider 就可以了;
  2. 在不知道原类型的情况下无法获取原注入的服务(命名服务会简单一些)
    所以我可以将所有注入的委托服务都转换为MethodInfo类型注入,
    这个可以有两种方式干涉
    a. 重新实现 ServiceCollectionAdd 操作中替换
    b. 在编译BuildServiceProvdier之前做,循环所有注入的服务,并替换其中的委托
    a方案代价太大,而且在使用上也有限制,所以我选择了b方案

最终我使用了一个新的扩展方法BuildSupportDelegateServiceProvdier来代替BuildServiceProvdier

改造后的逻辑大概是这样的

三、

扩展方法 BuildSupportDelegateServiceProvdier

public static class DelegateServiceProvdierExtensions
{
    /// <summary>
    /// 构造支持委托转换的服务提供程序
    /// </summary>
    /// <param name="services"></param>
    /// <returns></returns>
    public static IServiceProvider BuildSupportDelegateServiceProvdier(this IServiceCollection services)
    {
        var methodServices = new List<ServiceDescriptor>();
        // 循环所有服务
        foreach (var item in services)
        {
            if (typeof(Delegate).IsAssignableFrom(item.ServiceType))
            {
                // 针对委托类型的服务做一次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服务
        var provider = services.BuildServiceProvider();
        return new DelegateServiceProvdier(provider); // 返回装饰类
    }
}

DelegateServiceProvdier 装饰类

/// <summary>
/// 可创建委托服务的提供程序
/// </summary>
internal class DelegateServiceProvdier : IServiceProvider
{
    readonly IServiceProvider _provider;
    readonly ILogger _logger;
    readonly ConcurrentDictionary<Type, object> _services;

    /// <summary>
    /// 构造一个服务提供程序的代理
    /// </summary>
    /// <param name="provider">被代理的服务提供程序</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>
    /// 获取指定类型的服务
    /// </summary>
    /// <param name="serviceType">服务类型</param>
    /// <returns></returns>
    public object GetService(Type serviceType)
    {
        if (typeof(IServiceProvider) == serviceType)
        {
            return this;
        }
        // 从 _provider 中获取服务
        var service = _provider.GetService(serviceType);


        if (service == null)
        {
            // 当常规方式没有获取到服务,且服务是委托类型时,尝试获取MethodInfo服务,并返回最后一个签名相同的MethodInfo并转换为指定类型的委托
            return typeof(Delegate).IsAssignableFrom(serviceType)
                    ? _services.GetOrAdd(serviceType, x => ConvertDelegate(x, _provider.GetServices<MethodInfo>()))
                    : null;
        }

        if (service is Delegate delegateService)
        {
            // 当获取的服务是委托,但与要求的类型不符时,尝试转换委托类型
            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)
        {
            // 当获取的服务是泛型集合时
            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>
    /// 转换委托服务集合
    /// </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>
    /// 转换委托服务
    /// </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;
    }
}

四、

这里有一个坑,需要单开一节来说下

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

之前代码中的 TypeComparer.Instance 其实是一个自定义实现

原因是

在获取服务集合时(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); 这个操作上

仔细看t1t2的类型是不同的,关键点在于NamedType并非系统RuntimeType类型的对象


这里的代码,每次都会new一个新的TypeBuilderInstantiation,如果不重新实现IEqualityComparer<Type>,使用默认方式比较,即使2个属性完全相同的TypeBuilderInstantiation也无法得到相同的HashCode,将造成服务缓存无法正常工作

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;
    }
}

五、

最后项目结构是这样的



测试一下


据说,依赖注入时命名服务委托转换更配哦。。。

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

上一篇下一篇

猜你喜欢

热点阅读