.NET Core 依赖注入改造(2)- 委托转换
.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个需要解决的问题
- 做委托类型转换
这个只能在最后一步GetService
时,先获取原来注入的服务,然后根据新的Type进行类型转换;
这里只要替换掉原来的ServiceProvider
就可以了; - 在不知道原类型的情况下无法获取原注入的服务(命名服务会简单一些),
所以我可以将所有注入的委托服务都转换为MethodInfo
类型注入,
这个可以有两种方式干涉
a. 重新实现ServiceCollection
在Add
操作中替换
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);
这个操作上
仔细看t1
和t2
的类型是不同的,关键点在于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