.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容器创建实现并和参数一起传递进去,从而实现通过委托调用到具体的实现并返回
结语:当前这套方式只是一个简易的粗糙的实现,大家可以多思考一下是否有更优雅的办法,欢迎评论区留言讨论~