.netcore ioc 循環依賴問題及其相關思考之DispatchProxy
- 2021 年 4 月 15 日
- 筆記
.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容器創建實現並和參數一起傳遞進去,從而實現通過委託調用到具體的實現並返回
結語:當前這套方式只是一個簡易的粗糙的實現,大家可以多思考一下是否有更優雅的辦法,歡迎評論區留言討論~