.netcore ioc 循环依赖问题及其相关思考之DispatchProxy
.netcore引入了ioc机制让开发人员逐步习惯从过去的各种new对象变成通过IOC框架来管理对象的生命周期。这样当我们需要某个对象的时候,我们一般在构造函数里申明该对象的接口,即可通过ioc容器创建它。但是ioc正常工作的前提是假设我们对ioc使用是正确的,如果不正确的使用ioc则会带来某些意想不到的灾难,比如:
InvalidOperationException: A circular dependency was detected for the service of type 'xxx'
熟悉.netcore开发的同学应该在初期都会或多或少遇到过这个问题,这个异常代表着你的的ioc容器在创建对象时检测到了对象的循环依赖。简单来讲也就是假设a、b两个对象的构造函数中a依赖了b同时b依赖了a,或者a依赖了b同时b依赖了c同时c依赖了a,ioc容器在创建对象时如果检查不通过则会抛出上面这个异常。解决异常的办法很简单将相关的依赖移除即可,如果在依赖里包含必要的方法调用可以抽离成独立的类型来避免循环依赖,这里不展开讲。今天想说的是假设我们在某些极端的情况下必须要循环依赖时,通过ioc容器如何处理这样的问题。
如果我们在构造函数里让ioc框架并不去实例化接口对应的实现,而是在具体调用接口方法时才实例化,是否可以解决这个问题呢?聪明的同学应该能够想到通过懒加载的方式应该是可以实现的。不过今天我要讲的并不是Lazy<T>而是通过代理类来实现。下面就是看如何扩展我们的ioc方法来实现一个粗糙版本的懒加载。
首先我们创建一个空的webhost并申明两个接口
public interface IAService
{
Task<string> GetWords();
}
public interface IBService
{
Task<string> GetWords();
}
接着我们对这两个接口编写对应的实现
public class AService : IAService
{
public AService(IBService callService)
{ }
public async Task<string> GetWords()
{
return await Task.FromResult($"this is AService.GetTest");
}
}
public class BService : IBService
{
public BService(IAService testService)
{ }
public async Task<string> GetWords()
{
return await Task.FromResult($"this is BService.GetTest");
}
}
然后我们在Startup的ConfigureServices里通过默认的ioc框架注册这两个对象
services.AddScoped<IAService, AService>();
services.AddScoped<IBService, BService>();
接下来我们在某个控制器的构造函数里注入IAService
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
IAService aService;
public TestController(IAService aService)
{
this.aService = aService;
} [HttpGet]
public async Task<string> Get()
{
return await aService.GetWords();
}
}
此时运行控制台程序。顺利的话当我们访问该控制器下的action时,页面会返回 500 Internal Server Error,同时我们的控制台会打印一个unhandled exception内容如下:
现在我们来思考一下,如何通过扩展代理的方式来解决这个问题,注意这里仅演示接口-实现的注册方式,直接通过ioc框架将实现-实现注册到容器里并不在本次演示范围内
首先我们创建一个代理类实现
public class LazyProxy<TInterface, TImpl> : DispatchProxy
{
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
return "this is lazyservice";
}
}
接着我们扩展一下IServiceCollection的方法,让我们可以愉快的通过扩展方法来注入代理(*演示仅扩展了Scoped)
public static class ServiceCollectionExtension
{
public static IServiceCollection AddScopedLazy<TInterface, TImpl>(this IServiceCollection services) where TInterface : class where TImpl : class, TInterface
{
services.AddScoped(typeof(TInterface), x =>
{
var t_proxy = DispatchProxy.Create<TInterface, LazyProxy<TInterface, TImpl>>();
return t_proxy;
});
services.AddScoped(typeof(TImpl), typeof(TImpl));
return services;
}
}
可以看到这个扩展方法我们并没有将接口和实现作为一对键值对注册到容器中,而是通过接口和代理实例作为一对,实现和实现自己注册成了一对。接下来我们再次访问控制器时正确响应“this is lazyservice”,也就是我们代理类型的Invoke起作用了。其实演示到这里基本已经达到效果了,聪明的你应该能想到在invoke里通过ioc容器将实现类实现出来并调用实现类的同名方法即可调用到真正的实现,从而避免了循环依赖的产生,下面是简单粗暴版:
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
var impl = serviceProvider.GetService<TImpl>();
return typeof(TImpl).GetMethod(targetMethod.Name).Invoke(impl, args);
}
再次运行,页面会正确响应“this is AService.GetTest”。接下来看看如何优雅的实现,避免反射性能问题
优雅的实现有很多方式,比如通过MethodInfo.CreateDelegate的方式构造一个该对象的匿名委托,好处是代码实现比较简单,但是坏处也比较明显,构造委托时必须实例化一个当前类型的实例。同时CreateDelegate本身会有一个性能消耗,如果每次在代理的invoke里去创建方法委托其实并无多大意义,性能上甚至不如反射。如果是提前构造好委托则由于我们构造委托时创建好了实例,导致该实例不会被IOC容器管理起来会有生命周期的问题。第二种则是目前我实现的方法,通过表达式树的方式构造一个匿名委托。代码如下:
首先我们创建一个委托的工厂类DynamicMethodFactory并提供两个方法,第一个方法用于注册委托并添加到私有字典里,第二个方法用于通过方法查询字典获取委托
public static class DynamicMethodFactory
{
static Dictionary<MethodInfo, dynamic> DynamicMethods = new Dictionary<MethodInfo, dynamic>();
public static void CreateDynamicMethods<TInterface, TImpl>()
{
foreach (var item in typeof(TImpl).GetMethods().Where(x => x.IsFinal))
{
var key = typeof(TInterface).GetMethods().FirstOrDefault(x => x.Name == item.Name && x.ReturnType == item.ReturnType && x.GetParameters().Select(x => x.ParameterType).SequenceEqual(item.GetParameters().Select(x => x.ParameterType)));
DynamicMethods.TryAdd(key, ExpressionDelegateBuilder.CreateMethodDelegate(typeof(TImpl), item));
}
}
public static object CallDynamicMethod<TImpl>(MethodInfo method, TImpl service, object[] args)
{
DynamicMethods.TryGetValue(method, out dynamic func);
switch (args.Length)
{
default:
throw new ArgumentOutOfRangeException();
case 0:
return func(service);
case 1:
return func(service, args[0]);
case 2:
return func(service, args[0], args[1]);
case 3:
return func(service, args[0], args[1], args[2]);
case 4:
return func(service, args[0], args[1], args[2], args[3]);
case 5:
return func(service, args[0], args[1], args[2], args[3], args[4]);
case 6:
return func(service, args[0], args[1], args[2], args[3], args[4], args[5]);
case 7:
return func(service, args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
case 8:
return func(service, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
case 9:
return func(service, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
case 10:
return func(service, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
case 11:
return func(service, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10]);
case 12:
return func(service, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12]);
case 13:
return func(service, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13]);
case 14:
return func(service, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14]);
case 15:
return func(service, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15]);
}
}
}
接着我们创造一个表达式树的构造类ExpressionDelegateBuilder用于工厂创建具体的委托
public static class ExpressionDelegateBuilder
{
public static dynamic CreateMethodDelegate(Type instanceType, MethodInfo method)
{
var callmethod = typeof(ExpressionDelegateBuilder).GetMethods().Where(x => x.ReturnType == typeof(void) ? x.Name == nameof(ExpressionDelegateBuilder.CreateActionMethodDelegate) : x.Name == nameof(ExpressionDelegateBuilder.CreateFuncMethodDelegate)).ToArray()[method.GetParameters().Count()];
var genericType = new List<Type>();
genericType.Add(instanceType);
foreach (var item in method.GetParameters())
{
genericType.Add(item.ParameterType);
}
genericType.Add(method.ReturnType);
return callmethod.MakeGenericMethod(genericType.ToArray()).Invoke(null, new[] { method });
}
public static Func<TObj, Tout> CreateFuncMethodDelegate<TObj, Tout>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Func<TObj, Tout>>(reExpression, pParameter).Compile();
}
public static Func<TObj, dynamic, Tout> CreateFuncMethodDelegate<TObj, T1, Tout>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Func<TObj, dynamic, Tout>>(reExpression, pParameter).Compile();
}
public static Func<TObj, dynamic, dynamic, Tout> CreateFuncMethodDelegate<TObj, T1, T2, Tout>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Func<TObj, dynamic, dynamic, Tout>>(reExpression, pParameter).Compile();
}
public static Func<TObj, dynamic, dynamic, dynamic, Tout> CreateFuncMethodDelegate<TObj, T1, T2, T3, Tout>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Func<TObj, dynamic, dynamic, dynamic, Tout>>(reExpression, pParameter).Compile();
}
public static Func<TObj, dynamic, dynamic, dynamic, dynamic, Tout> CreateFuncMethodDelegate<TObj, T1, T2, T3, T4, Tout>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Func<TObj, dynamic, dynamic, dynamic, dynamic, Tout>>(reExpression, pParameter).Compile();
}
public static Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, Tout> CreateFuncMethodDelegate<TObj, T1, T2, T3, T4, T5, Tout>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, Tout>>(reExpression, pParameter).Compile();
}
public static Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout> CreateFuncMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, Tout>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout>>(reExpression, pParameter).Compile();
}
public static Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout> CreateFuncMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, Tout>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout>>(reExpression, pParameter).Compile();
}
public static Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout> CreateFuncMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, T8, Tout>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout>>(reExpression, pParameter).Compile();
}
public static Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout> CreateFuncMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, T8, T9, Tout>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout>>(reExpression, pParameter).Compile();
}
public static Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout> CreateFuncMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, Tout>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout>>(reExpression, pParameter).Compile();
}
public static Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout> CreateFuncMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, Tout>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout>>(reExpression, pParameter).Compile();
}
public static Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout> CreateFuncMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, Tout>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout>>(reExpression, pParameter).Compile();
}
public static Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout> CreateFuncMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, Tout>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout>>(reExpression, pParameter).Compile();
}
public static Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout> CreateFuncMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, Tout>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout>>(reExpression, pParameter).Compile();
}
public static Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout> CreateFuncMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, Tout>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Func<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, Tout>>(reExpression, pParameter).Compile();
}
public static Action<TObj> CreateActionMethodDelegate<TObj>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Action<TObj>>(reExpression, pParameter).Compile();
}
public static Action<TObj, dynamic> CreateActionMethodDelegate<TObj, T1>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Action<TObj, dynamic>>(reExpression, pParameter).Compile();
}
public static Action<TObj, dynamic, dynamic> CreateActionMethodDelegate<TObj, T1, T2>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Action<TObj, dynamic, dynamic>>(reExpression, pParameter).Compile();
} public static Action<TObj, dynamic, dynamic, dynamic> CreateActionMethodDelegate<TObj, T1, T2, T3>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Action<TObj, dynamic, dynamic, dynamic>>(reExpression, pParameter).Compile();
}
public static Action<TObj, dynamic, dynamic, dynamic, dynamic> CreateActionMethodDelegate<TObj, T1, T2, T3, T4>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Action<TObj, dynamic, dynamic, dynamic, dynamic>>(reExpression, pParameter).Compile();
}
public static Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic> CreateActionMethodDelegate<TObj, T1, T2, T3, T4, T5>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic>>(reExpression, pParameter).Compile();
}
public static Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic> CreateActionMethodDelegate<TObj, T1, T2, T3, T4, T5, T6>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic>>(reExpression, pParameter).Compile();
}
public static Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic> CreateActionMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic>>(reExpression, pParameter).Compile();
}
public static Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic> CreateActionMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, T8>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic>>(reExpression, pParameter).Compile();
}
public static Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic> CreateActionMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, T8, T9>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic>>(reExpression, pParameter).Compile();
}
public static Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic> CreateActionMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic>>(reExpression, pParameter).Compile();
}
public static Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic> CreateActionMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic>>(reExpression, pParameter).Compile();
}
public static Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic> CreateActionMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic>>(reExpression, pParameter).Compile();
}
public static Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic> CreateActionMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic>>(reExpression, pParameter).Compile();
}
public static Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic> CreateActionMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic>>(reExpression, pParameter).Compile();
}
public static Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic> CreateActionMethodDelegate<TObj, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(MethodInfo method)
{
var reExpression = GetReturnExpression(typeof(TObj), method, out var pParameter);
return Expression.Lambda<Action<TObj, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic, dynamic>>(reExpression, pParameter).Compile();
}
static UnaryExpression GetReturnExpression(Type objType, MethodInfo method, out List<ParameterExpression> parameterExpressions)
{
var instance = Expression.Parameter(objType, "p");
parameterExpressions = new List<ParameterExpression>();
parameterExpressions.Add(instance);
var mcparamExpression = new List<UnaryExpression>();
foreach (var item in method.GetParameters())
{
var pParameter = Expression.Parameter(typeof(object), "a");
parameterExpressions.Add(pParameter);
mcparamExpression.Add(Expression.Convert(pParameter, item.ParameterType));
}
return Expression.Convert(Expression.Call(instance, method, mcparamExpression.ToArray()), method.ReturnType);
}
}
最后我们改造一下AddScopedLazy以及LazyProxy
AddScopedLazy:
public static IServiceCollection AddScopedLazy<TInterface, TImpl>(this IServiceCollection services) where TInterface : class where TImpl : class, TInterface
{
services.AddScoped(typeof(TInterface), x =>
{
var t_proxy = DispatchProxy.Create<TInterface, LazyProxy<TInterface, TImpl>>() as LazyProxy<TInterface, TImpl>;
t_proxy.serviceProvider = x;
return t_proxy;
});
services.AddScoped(typeof(TImpl), typeof(TImpl));
DynamicMethodFactory.CreateDynamicMethods<TInterface, TImpl>();
return services;
}
LazyProxy:
public class LazyProxy<TInterface, TImpl> : DispatchProxy
{
public IServiceProvider serviceProvider;
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
return DynamicMethodFactory.CallDynamicMethod(targetMethod, serviceProvider.GetService<TImpl>(), args);
}
}
这样我们在启动时会构造一个func<tservice,tin1,tin2,tin....,tout> 这样的匿名委托,当调用代理类的Invoke时,我们会通过工厂获取到这个匿名委托,同时将通过ioc容器创建实现并和参数一起传递进去,从而实现通过委托调用到具体的实现并返回
结语:当前这套方式只是一个简易的粗糙的实现,大家可以多思考一下是否有更优雅的办法,欢迎评论区留言讨论~
.netcore ioc 循环依赖问题及其相关思考之DispatchProxy的更多相关文章
- Spring IoC 循环依赖的处理
前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 是 ...
- Spring IoC - 循环依赖
Spring 复习 3.循环依赖 3.1 定义 循环依赖指多个对象的创建过程中均需要注入对方对象,如下所示 class A{ B b; public A(){ } public A(B b){ thi ...
- 曹工说Spring Boot源码(29)-- Spring 解决循环依赖为什么使用三级缓存,而不是二级缓存
写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...
- Spring的循环依赖,学就完事了【附源码】
目录 啥是循环依赖? Spring可以解决循环依赖的条件 Spring如何去解决循环依赖 SpringBean的创建流程 Spring维护的三级缓存 getSingleton getSingleton ...
- Spring IOC 容器源码分析 - 循环依赖的解决办法
1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...
- Spring框架系列(8) - Spring IOC实现原理详解之Bean实例化(生命周期,循环依赖等)
上文,我们看了IOC设计要点和设计结构:以及Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的:容器中存放的是Bean的定义即Be ...
- My.Ioc 代码示例——避免循环依赖
本文的目的在于通过一些示例,向大家说明 My.Ioc 支持哪些类型的依赖关系.也就是说,如何设计对象不会导致循环依赖. 在 Ioc 世界中,循环依赖是一个顽敌.这不仅因为它会导致 Ioc 容器抛出异常 ...
- 展开说说,Spring Bean IOC、AOP 循环依赖
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 延迟满足能给你带来什么? 大学有四年时间,但几乎所有人都是临近毕业才发现找一份好工作 ...
- Spring Ioc源码分析系列--自动注入循环依赖的处理
Spring Ioc源码分析系列--自动注入循环依赖的处理 前言 前面的文章Spring Ioc源码分析系列--Bean实例化过程(二)在讲解到Spring创建bean出现循环依赖的时候并没有深入去分 ...
随机推荐
- 2020 NGK 全球启动大会于美国硅谷圆满落幕
据NCC报道美国西海岸时间11月25日,NGK全球启动大会在美国加利福尼亚北部的硅谷会展中心成功举办.本次大会吸引了来自世界各地的企业家.创业者.开发者,以及投资人达一万人次齐聚硅谷. NGK创始人. ...
- 授权认证登录之 Cookie、Session、Token、JWT 详解
一.先了解几个基础概念 什么是认证(Authentication) 通俗地讲就是验证当前用户的身份. 互联网中的认证: 用户名密码登录 邮箱发送登录链接 手机号接收验证码 只要你能收到邮箱/验证码,就 ...
- 看完我的笔记不懂也会懂----MarkDown使用指南
目录 语法 [TOC] 自动生成目录 1. 标题 2. 文本强调 3. 列表 4. 图片 5. 超链接 6. 文本引用 7. 分割线 8. 代码 9. 任务列表 (MPE专属) 10. 表格 11. ...
- Windows常用快捷键和基本dos命令
Windows常用快捷键 键盘功能键:Tab,Shift,Ctrl,Alt,空格,Enter,Window... 键盘快捷键: 全选:Ctrl+A 复制: Ctrl+C 粘贴: Ctrl+V 撤销: ...
- 测试平台系列(2) 给Pity添加配置
给Pity添加配置 回顾 还记得上篇文章创立的「Flask」实例吗?我们通过这个实例,给根路由 「/」 绑定了一个方法,从而使得用户访问不同路由的时候可以执行不同的方法. 配置 要知道,在一个「Web ...
- C语言之三字棋的简单实现及扩展
C语言之三字棋的简单实现及扩展 在我们学习完数组之后,我们完全可以利用数组相关知识来写一个微小型的游戏,比如说今天所说的--三子棋. 大纲: 文件组成 实现 完整代码展示 扩展 即: 一.文件 ...
- 对于如何从SM2的pfx证书文件中解析公钥和私钥,并从二次加密的密文中解密
首先呢,由于我的域名之前处理点问题,然后备案第二个网站时候,第一个网站没法访问,所以备案没过,阿里云告诉我要删除一个网站的备案,但是他没告诉我要删除主体,所以我的备案主体成了空壳主体,要传真或者发快递 ...
- LNMP配置——Nginx配置 —— 用户认证
一.配置 再来创建一个新的虚拟主机 #cd /usr/local/nginx/conf/vhost #vi test.com.conf 写入: server { listen 80; server_n ...
- golang float32/64转string
v := 3.1415926535 s1 := strconv.FormatFloat(v, 'E', -1, 32)//float32s2 := strconv.FormatFloat(v, 'E' ...
- golang 实现最小二乘法拟合直线
func LeastSquares(x[]float64,y[]float64)(a float64,b float64){ // x是横坐标数据,y是纵坐标数据 // a是斜率,b是截距 xi := ...