.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個需要解決的問題
- 做委托類型轉(zhuǎn)換
這個只能在最后一步GetService時,先獲取原來注入的服務(wù),然后根據(jù)新的Type進(jìn)行類型轉(zhuǎn)換;
這里只要替換掉原來的ServiceProvider就可以了; - 在不知道原類型的情況下無法獲取原注入的服務(wù)(命名服務(wù)會簡單一些),
所以我可以將所有注入的委托服務(wù)都轉(zhuǎn)換為MethodInfo類型注入,
這個可以有兩種方式干涉
a. 重新實(shí)現(xiàn)ServiceCollection在Add操作中替換
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>),的一個特殊對象TypeBuilderInstantiationpublic 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ì)看t1和t2的類型是不同的,關(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