.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容器建立實現並和引數一起傳遞進去,從而實現通過委託呼叫到具體的實現並返回
結語:當前這套方式只是一個簡易的粗糙的實現,大家可以多思考一下是否有更優雅的辦法,歡迎評論區留言討論~