全新升級的AOP框架Dora.Interception[6]: 框架設計和實現原理

本系列前面的五篇文章主要介紹Dora.Interception的編程模式以及對它的擴展定制,現在我們來聊聊它的設計和實現原理。

目錄
一、調用鏈抽象
二、基于約定的攔截器定義
三、基于調用上下文的依賴注入容器
四、攔截器的提供
五、調用鏈的構建
六、方法攔截的實現原理
七、依賴注入框架的整合
八、看看生成的代理類

一、調用鏈抽象

從設計模式來看,Dora.Interception采用了“職責鏈”模式。我們將應用到同一個方法的多個攔截器以及針對目標方法的調用構建成如下所示的“調用鏈”。調用鏈在執行過程中共享同一個“調用上下文”,后者提供當前調用的上下文信息,比如目標對象、調用方法、輸出參數和返回值等。每個攔截器不僅可以利用這些上下文信息執行對應的操作,還可以直接利用此上下文修改參數和返回值,并且自行決定是否繼續執行后續調用。

63f2b89a0ab36677051fadc7654a6d63.png

我們定義了如下這個抽象類InvocationContext來表示上述的調用上下文。對于參數/返回值的提取,我們設計成抽象方法以避免因裝箱/拆箱帶來的性能問題。攔截器針對其他服務的依賴是一個基本的需求,所以我們為InvocationContext定義了一個InvocationServices屬性來提供針對當前調用的IServiceProvider對象。在默認情況下,我們會為每次調用創建一個服務范圍,并利用此范圍的IServiceProvider對象作為這個InvocationServices屬性的值。但是對于ASP.NET Core應用,我們會直接使用針對當前請求的IServiceProvider對象。

public?abstract?class?InvocationContext
{public?object?Target?{?get;?}?=?default!;public?abstract?MethodInfo?MethodInfo?{?get;?}public?abstract?IServiceProvider?InvocationServices?{?get;?}public?IDictionary<object,?object>?Properties?{?get;?}public?abstract?TArgument?GetArgument<TArgument>(string?name);public?abstract?TArgument?GetArgument<TArgument>(int?index);public?abstract?InvocationContext?SetArgument<TArgument>(string?name,?TArgument?value);public?abstract?InvocationContext?SetArgument<TArgument>(int?index,?TArgument?value);public?abstract?TReturnValue?GetReturnValue<TReturnValue>();public?abstract?InvocationContext?SetReturnValue<TReturnValue>(TReturnValue?value);protected?InvocationContext(object?target);internal?InvokeDelegate?Next?{?get;?set;?}?=?default!;public?ValueTask?ProceedAsync()?=>?Next.Invoke(this);
}

既然有了這樣一個能夠體現當前方法調用上下文的InvocationContext類型,那么上述的“調用量”就可以表示成如下這個InvokeDelegate委托。熟悉ASP.NET Core的讀者可以看出Dora.Interception的調用鏈設計與ASP.NET Core框架的“中間件管道”幾乎一致,InvocationContext和InvokeDelegate分別對應后者的HttpContext和RequestDelegate。

public?delegate?ValueTask?InvokeDelegate(InvocationContext?context);

既然將ASP.NET Core作為類比,Dora.Interception的攔截器自然就對應著ASP.NET Core的中間件了。我們知道后者體現為一個Func<RequestDelegate, RequestDelegate>委托,作為輸入的RequestDelegate代表由后續中間件構建的請求處理管道,每個中間件需要利用此對象將請求分發給后續管道進行處理。Dora.Interception采用了更為簡單的設計,我們將攔截器也表示成上述的InvokeDelegate委托,因為針對后續攔截器以及目標方法的調用可以利用代表調用上下文的InvocationContext對象的ProceedAsync方法來完成。如上面的代碼片段所示,InvocationContext具有一個名為Next的內部屬性用來表示調用調用鏈的下一個InvokeDelegate對象,每個攔截器在執行之前,此屬性都會預先被設置,ProceedAsync方法調用的正式此屬性返回的InvokeDelegate對象。

二、基于約定的攔截器定義

雖然攔截器最終由一個InvokeDelegate委托來表示,但是將其定義成一個普通的類型具有更好的編程體驗。考慮到動態注入依賴服務的需要,我們并沒有為攔截器定義任何的接口和基類,而是采用基于約定的定義方式。這一點與ASP.NET Core基于約定的中間件定義方法類似,由于我們的攔截器委托比中間件委托要簡潔,基于約定的攔截器自然比定義中間件要簡單。中間件定義按照如下的約定即可:

  • 將中間件定義成一個可以被依賴注入容器實例化的類型,一般定義成公共實例類型即可;

  • 構造函數的選擇由依賴注入容器決定,構造函數可以包含任意參數;

  • 攔截操作定義在一個方法類型為ValueTask并被命名為InvokeAsync的異步方法中,該方法必須包含一個表示當前調用上下文的InvocationContext類型的參數,該參數在參數列表的位置可以任意指定。

  • InvokeAsync方法可以注入任意能夠從依賴注入容器提供的對象。

按照約定定義的中間件類型或者此類型的對象最終都需要轉換成一個InvokeDelegate對象,此項功能體現在IConventionalInterceptorFactory接口的兩個CreateInterceptor重載方法上。第一個重載的arguments將被作為調用構造函數的參數,對于依賴注入容器無法提供的參數必須在此指定。內部類型ConventionalInterceptorFactory以表達式樹的形式實現了這個接口,具體實現就不在這里展示了,有興趣的朋友可以查看源代碼。

public?interface?IConventionalInterceptorFactory
{InvokeDelegate?CreateInterceptor(Type?interceptorType,?params?object[]?arguments);InvokeDelegate?CreateInterceptor(object?interceptor);
}internal?sealed?class?ConventionalInterceptorFactory?:?IConventionalInterceptorFactory
{public?InvokeDelegate?CreateInterceptor(Type?interceptorType,?params?object[]?arguments);public?InvokeDelegate?CreateInterceptor(object?interceptor);
}

三、基于調用上下文的依賴注入容器

InvocationContext的InvocationServices屬性返回針對當前調用上下文的依賴注入容器。在默認的情況下,我們會在創建InvocationContext上下文的時候創建一個服務范圍,并使用此范圍的IServiceProvider對象作為其InvocationServices屬性。注入到InvokeAsync方法中的依賴服務是在調用時利用此IServiceProvider對象動態提供的,我們也可以在實現的InvokeAsync方法中安全的使用此對象來提供所需的服務實例。由于服務范圍會在調用結束之后被自動終結,所以非單例服務實例能夠被正常回收。

如下所示的IInvocationServiceScopeFactory接口表示用來創建上述服務范圍的工廠,代表服務范圍的IServiceScope對象由其CreateInvocationScope方法創建,InvocationServiceScopeFactory是對該接口的默認實現。

public?interface?IInvocationServiceScopeFactory
{IServiceScope?CreateInvocationScope();
}internal?class?InvocationServiceScopeFactory?:?IInvocationServiceScopeFactory
{private?readonly?IApplicationServicesAccessor?_applicationServicesAccessor;public?InvocationServiceScopeFactory(IApplicationServicesAccessor?applicationServicesAccessor)?=>?_applicationServicesAccessor?=?applicationServicesAccessor????throw?new?ArgumentNullException(nameof(applicationServicesAccessor));public?IServiceScope?CreateInvocationScope()=>?_applicationServicesAccessor.ApplicationServices.CreateScope();
}

如果在一個ASP.NET Core應用中,我們因為針對當前請求的IServiceProvider(RequestServices)對象作為調用上下文的InvocationServices也許更為適合,所以在ASP.NET Core應用中注冊的IInvocationServiceScopeFactory實現類型為如下這個RequestServiceScopeFactory 類型。

internal?class?RequestServiceScopeFactory?:??IInvocationServiceScopeFactory
{private?readonly?InvocationServiceScopeFactory?_factory;private?readonly?IHttpContextAccessor??_httpContextAccessor;private?NullServiceScope??_nullServiceScope;public?RequestServiceScopeFactory(IServiceProvider??serviceProvider,?IHttpContextAccessor?httpContextAccessor){_factory?=?ActivatorUtilities.CreateInstance<InvocationServiceScopeFactory>(serviceProvider????throw?new?ArgumentNullException(nameof(serviceProvider)));_httpContextAccessor?=?httpContextAccessor????throw?new?ArgumentNullException(nameof(httpContextAccessor));}public?IServiceScope?CreateInvocationScope(){_nullServiceScope???=?new?NullServiceScope?(_httpContextAccessor);return?_httpContextAccessor.HttpContext?==?null??_factory.CreateInvocationScope():?_nullServiceScope;}private?class?NullServiceScope?:?IServiceScope{private?readonly?IHttpContextAccessor?_httpContextAccessor;public?NullServiceScope(IHttpContextAccessor?httpContextAccessor)?=>?_httpContextAccessor?=?httpContextAccessor;public?IServiceProvider?ServiceProvider?=>?_httpContextAccessor.HttpContext?.RequestServices!;public?void?Dispose()?{?}}
}

四、攔截器的提供

我們利用如下這個IInterceptorProvider接口來表示攔截器的“提供者”,它定義的GetInterceptors方法為指定類型的方法提供一組可供排序的攔截器,該方法返回一組Sortable<InvokeDelegate>對象,每個Sortable<InvokeDelegate>對象的Value屬性代表作為攔截器的InvokeDelegate委托,Order屬性用來對攔截器進行排序。

public?interface?IInterceptorProvider
{bool?CanIntercept(Type?targetType,?MethodInfo?method,?out?bool?suppressed);IEnumerable<Sortable<InvokeDelegate>>?GetInterceptors(Type?targetType,?MethodInfo?method);void?Validate(Type?targetType,?Action<MethodInfo>?methodValidator,?Action<PropertyInfo>?propertyValidator)?;
}public?sealed?class?Sortable<T>
{public?int?Order?{?get;?}public?T?Value?{?get;?set;?}public?Sortable(int?order,?T?value){Order?=?order;Value?=?value;}
}

IInterceptorProvider旨在為指定的方法提供攔截器,所以它體現的是針對攔截器的注冊,即采用怎樣的方式將攔截器應用到期望的目標方法上。根據Dora.Interception的實現原理,并不是每一個方法都能被攔截,所以我們為IInterceptorProvider定義了一個Validate方法用來驗證被應用到指定方法或者屬性上的攔截器是否有效。具體的驗證邏輯無需自行實現,只需要調用該方法提供的兩個作為驗證器的參數(methodValidator和propertyValidator)就可以了。這樣做的好處是今早確定我們針對某個方法的攔截意圖是否能夠生效,Dora.Interception提供的兩種原生的實現均實現了驗證功能,對于自定義的實現,可以根據需要決定是否需要驗證。

IInterceptorProvider接口還定義了CanIntercept方法用來確定指定類型的方法能否被攔截。一般來說,如果指定方法上沒有注冊攔截器,方法自然不會被攔截。但是很多時候我們需要顯式屏蔽掉某個方法、屬性甚至類型的攔截特性,我們認為這樣的設置具有最高優先級,所以即使被注冊了攔截器(包括被其他IInterceptorProvider注冊)也不能被攔截,輸出參數suppressed的作用就體現在這里。

由于攔截器大部分情況下都采用基于約定的類型來定義,所以針對攔截器的注冊對于最終用戶來說也應該針對攔截器類型或者實例進行,所以我們通過實現IInterceptorProvider接口定義了如下這個InterceptorProviderBase基類,它利用InterceptorFactory屬性返回的IConventionalInterceptorFactory方便我們將按照約定定義的攔截器類型或對應的對象轉換成標InvokeDelegate。

public?abstract?class?InterceptorProviderBase?:?IInterceptorProvider
{public?IConventionalInterceptorFactory?InterceptorFactory?{?get;?}protected?InterceptorProviderBase(IConventionalInterceptorFactory?interceptorFactory)=>?InterceptorFactory?=?interceptorFactory????throw?new?ArgumentNullException(nameof(interceptorFactory));public?abstract?bool?CanIntercept(Type?targetType,?MethodInfo?method,?out?bool?suppressed);public?abstract?IEnumerable<Sortable<InvokeDelegate>>?GetInterceptors(Type?targetType,?MethodInfo?method);
}

Dora.Interception默認實現了兩種攔截器的注冊方法就是由對應的IInterceptorProvider實現類型達成的。具體來說,DataAnnotationInterceptorProvider實現了針對特性標注的攔截器注冊,基于Lambda表達式的攔截器注冊方式由ExpressionInterceptorProvider來完成。

五、調用鏈的構建

如果我們能夠將針對目標方法的調用也轉換成一個InvokeDelegate委托,意味著針對整個攔截方法的調用鏈就由一系列InvokeDelegate委托構建而成,此項工作體現在如下這個IMethodInvokerBuilder接口的Build方法上,該方法旨在為指定的可攔截的方法創建一個代表方法調用鏈的InvokeDelegate對象,其三個參數分別代表目標類型、方法和用來完成目標方法調用的InvokeDelegate委托。IMethodInvokerBuilder接口還定義了一個CanIntercept方法用來確定指定的方法能否或者是否需要被攔截。

public?interface?IMethodInvokerBuilder
{InvokeDelegate?Build(Type?targetType,?MethodInfo?method,?InvokeDelegate?targetMethodInvoker);bool?CanIntercept(Type?targetType,?MethodInfo?method);
}

DefaultMethodInvokerBuilder實現了上面這個接口。如代碼片段所示,注冊的所有IInterceptorProvider對象被注入構造函數之中。在實現了Build方法中,它利用這些IInterceptorProvider對象得到注冊到指定方法的所有攔截器,并按照順序構建成一個由InvokeDelegate委托表示的調用鏈。確保在攔截器執行之前對“下一個InvokeDelegate”進行設置也是在這里完成的。另一個CanIntercept方法的實現就更簡單了,按照提供的邏輯:如果任何一個IInterceptorProvider對象將指定方法的攔截功能屏蔽掉,該方法就返回false,否則此方法的方法值體現的是是否由任何一個IInterceptorProvider對象決定攔截指定的方法。

internal?class?DefaultMethodInvokerBuilder?:?IMethodInvokerBuilder
{private?readonly?IEnumerable<IInterceptorProvider>?_interceptorProviders;private?readonly?Dictionary<Tuple<Type,?MethodInfo>,?Sortable<InvokeDelegate>[]>?_cache?=?new();public?DefaultMethodInvokerBuilder(IEnumerable<IInterceptorProvider>?interceptorProviders){_interceptorProviders?=?interceptorProviders????throw?new?ArgumentNullException(nameof(interceptorProviders));}public?InvokeDelegate?Build(Type?targetType,?MethodInfo?method,?InvokeDelegate?targetMethodInvoker){Guard.ArgumentNotNull(targetType);Guard.ArgumentNotNull(method);Guard.ArgumentNotNull(targetMethodInvoker);if?(!CanIntercept(targetType,?method)){throw?new?InterceptionException($"The?method?'{method.Name}'?of?'{targetType}'?cannot?be?interceptable.");}var?key?=?new?Tuple<Type,?MethodInfo>(targetType,?method);var?interceptors?=?_cache.TryGetValue(key,?out?var?value)??value!:?_cache[key]?=?_interceptorProviders.SelectMany(it?=>?it.GetInterceptors(targetType,?method)).OrderBy(it?=>?it.Order).ToArray();var?length?=?interceptors.Length;interceptors?=?Enumerable.Range(0,?length).Select(it?=>?new?Sortable<InvokeDelegate>(it,?interceptors[it].Value)).ToArray();Array.ForEach(interceptors,?Wrap);return?interceptors[0].Value;void?Wrap(Sortable<InvokeDelegate>?sortable){var?index?=?sortable.Order;var?interceptor?=?sortable.Value;sortable.Value?=?context?=>{context.Next?=?index?<?length?-?1???interceptors![index?+?1].Value?:?targetMethodInvoker;return?interceptor(context);};}}public?bool?CanIntercept(Type?targetType,?MethodInfo?method){Guard.ArgumentNotNull(targetType);Guard.ArgumentNotNull(method);bool?interceptable?=?false;foreach?(var?provider?in?_interceptorProviders){if?(provider.CanIntercept(targetType,?method,?out?var?suppressed)){interceptable?=?true;}if?(suppressed){return?false;}}return?interceptable;}
}

便于生成的代碼使用這個IMethodInvokerBuilder,我們把應用當前使用的IMethodInvokerBuilder對象賦值給如下這個MethodInvokerBuilder類型的靜態屬性Instance。

public?static?class?MethodInvokerBuilder
{public?static?IMethodInvokerBuilder?Instance?{?get;?internal?set;?}?=?default!;
}

六、方法攔截的實現原理

實現AOP需要將應用到某個方法的攔截器“注入”到針對該方法的調用中,其注入方式大體分兩類,一種是靜態注入,另一種動態注入。靜態注入是在編譯的時候直接將針對攔截器的調用代碼注入到目標方法中,這種注入方式對應用程序的運行不會帶來任何負擔,所以具有最好的性能,缺點就是無法應用一些動態的攔截策略。

動態注入則是在運行時注入攔截代碼,它同樣具有多種實現方式。很早之前,我們利用基于.NET Remoting的TranparentPoxy/RealProxy的方法動態分發機制可以很容易地實現針對指定方法的攔截,但是這種實現方式的性能堪憂。目前使用得最多就是采用IL Emit,它在IL語言的層面生成可被攔截的動態代理類。這種方式由于是直接面向IL編程,所以對于大部分編程人員來說都是一個不小的挑戰,Dora.Interception之前的版本即是采用這種實現方式。動態編譯還具有另一種方式,那就是利用CLR Profiler直接修改JIT生成的機器代碼,著名的APM框架DataDog就是利用這種方式實現針對各種組件的分布式跟蹤的。由于這種方式得用C++來寫,對.NET開發人員的要求就更高了。

最新版本的Dora.Interception放棄了基于IL Emit的實現方案,因為這樣的實現方式太過繁瑣,開發、維護、診斷和升級都是巨大的挑戰。一般來說,進行IL Emit編程都會先寫出生成代碼的C#形式,然后再將其轉換成IL代碼,如果我們能夠直接將C#代碼編譯成IL代碼,一切將會變得容易。實際上.NET的編譯平臺Roslyn本就可以將C#代碼編程成對應的程序集,所以Dora.Interception直接利用了這個能力。

不論是上面提到的針對TranparentPoxy/RealProxy的實現,還是基于IL Emit,我們都需要利用一個“容器”來生成一個代理對象(如果直接使用目標類型的實例,其方法調用自然無法被攔截)。對于.NET (Core)來說,依賴注入容器無疑就是這個代理對象最好的創建者,所以Dora.Interception選擇建立在依賴注入框架之上。對于注冊到每個服務,如果目標類型的方法上注冊了攔截器,我們會為它生成相應的代理類,并為此代理類生成對應的服務注冊來替換原來的服務注冊。

如果服務注冊采用接口+實現類型(IFoobar/Foobar)的形式,代碼生成器會采用如下的方式生成一個實現接口(IFoobar)同時封裝目標對象(Foobar)的代理類(FoobarProxy)。FoobarProxy會實現定義在接口中的所有成員,如果方法調用需要被攔截,針對攔截器的調用會實現在該方法中,否則它只需要直接調用封裝的對象即可。

d127c70acd69213507cb3236101f798b.png

如果服務注冊并未使用接口,那么Flight.Interception只能采用方法重寫的方式實現對方法調用的攔截,這意味著被攔截的方法只能是虛方法。如下圖所示,如果給定服務注冊的服務類型和實現類型均為Foobar,代碼生成器生成的代理類FoobarProxy是Foobar的子類,它會重寫需要攔截的方法來調用注冊的攔截器。

e61d38fa38f4f994568dce2ccf3c46ce.png

如果對于如下這個基于接口的服務注冊,如果某個需要攔截的方法并非接口方法。

public?interface?IFoobar
{Task?InvokeAsync(int?x,?int?y);
}public?class?Foobar?:?IFoobar
{[Interceptor(typeof(Interceptor1))]public?Task?InvokeAsync(int?x,?int?y)?=>?InvokeCoreAsync(x,?y);[Interceptor(typeof(Interceptor2))]protected?virtual?Task?InvokeCoreAsync(int?x,?int?y)?=>?Task.CompletedTask;
}

此時需要生成兩個代理類。其中FoobarProxy1派生于Foobar,利用重寫的InvokeCoreAsync方法解決針對非接口方法的攔截。FoobarProxy2實現IFoobar接口,并封裝FoobarProxy1對象,解決接口方法的攔截。

c51a0776fd24171a8ef4035dfb88bb52.png

七、依賴注入框架的整合

我們為代理類型的生成定義了如下這個ICodeGenerator接口作為代碼生成器。該接口的TryGenerate會為指定的服務注冊(ServiceDescriptor)生成的代理類型。如果需要生成代理類(可被攔截的方法上被注冊了任意攔截器)該方法返回True,生成的C#代碼寫入代表代碼生成上下文的CodeGenerationContext 對象,輸出參數proxyTypeNames返回生成的一個或者兩個代理類的全名。至于名一個RegiserProxyType方法則使用針對生成的代理類型來替換現有的服務注冊。

public?interface?ICodeGenerator
{bool?TryGenerate(ServiceDescriptor?serviceDescriptor,?CodeGenerationContext?codeGenerationContext,?out?string[]??proxyTypeNames);void?RegisterProxyType(IServiceCollection?services,?ServiceDescriptor?serviceDescriptor,?Type[]?proxyTypes);
}public?sealed?class?CodeGenerationContext
{public?ISet<Assembly>?References?{?get;?}public?int?IndentLevel?{?get;?private?set;?}public?string?SourceCode?{?get;?}public?CodeGenerationContext?WriteLines(params?string[]?lines);public?IDisposable?CodeBlock(string??start?=?null,?string??end?=?null)?;public?IDisposable?Indent()?;
}

Dora.Interception提供了針對ICodeGenerator接口的兩個內部實現類型(InterfaceProxyGenerator和VirtualMethodProxyGenerator),正式它們幫助我們生成了針對接口和虛方法的代理類型。由于涉及的實現比較繁瑣,具體實現就不再這里提供了,有興趣的朋友可以查看源代碼。

我們知道依賴注入框架可以利用自定義的IServiceProviderFactory實現類型整合第三方依賴注入框架,Dora.Interception針對依賴注入框架的整合也是基于這樣的實現。具體的實現類型就是如下這個InterceptableServiceProviderFactory 。

internal?class?InterceptableServiceProviderFactory?:?IServiceProviderFactory<InterceptableContainerBuilder>
{private?readonly?ServiceProviderOptions?_options;private?readonly?Action<InterceptionBuilder>??_setup;public?InterceptableServiceProviderFactory(ServiceProviderOptions?options,?Action<InterceptionBuilder>??setup){_options?=?options????throw?new?ArgumentNullException(nameof(options));_setup?=?setup;}public?InterceptableContainerBuilder?CreateBuilder(IServiceCollection?services)?=>?new(services,?_options,?_setup);public?IServiceProvider?CreateServiceProvider(InterceptableContainerBuilder?containerBuilder)?=>?containerBuilder.CreateServiceProvider();
}

InterceptableServiceProviderFactory 實現了IServiceProviderFactory<InterceptableContainerBuilder>接口,具體的實現體現作為泛型適配類型的InterceptableContainerBuilder類型上,如下就是該類型的定義。如代碼片段所示,在創建最終的IServiceProvider對象之前,InterceptableContainerBuilder會利用提供的ICodeGenerator針對每個服務注冊進行可攔截代理類型的生成。如果某個ICodeGenerator真正生成相應的代理類型,它最終還會負責完成該代理類型的注冊。最終的IServiceProvider對象根據調整好的服務注冊構建而成。

public?sealed?class?InterceptableContainerBuilder
{private?readonly?IServiceCollection?_services;private?readonly?ServiceProviderOptions?_serviceProviderOptions;public?InterceptableContainerBuilder(IServiceCollection?services,?ServiceProviderOptions?serviceProviderOptions,?Action<InterceptionBuilder>??setup){_services?=?Guard.ArgumentNotNull(services);services.AddInterception(setup);services.AddSingleton<IServiceLifetimeProvider>(new?ServiceLifetimeProvider(services));_serviceProviderOptions?=?serviceProviderOptions????throw?new?ArgumentNullException(nameof(serviceProviderOptions));}public?IServiceProvider?CreateServiceProvider(){var?provider?=?_services.BuildServiceProvider();try{var?applicationServiceAccessor?=?provider.GetRequiredService<IApplicationServicesAccessor>();((ApplicationServicesAccessor)?applicationServiceAccessor).ApplicationServices?=?provider;MethodInvokerBuilder.Instance?=?provider.GetRequiredService<IMethodInvokerBuilder>();var?logger?=?provider.GetRequiredService<ILogger<InterceptableContainerBuilder>>();var?log4GenerateCode?=?LoggerMessage.Define<string>(LogLevel.Information,?0,?"Interceptable?proxy?classes?are?generated.?"?+?Environment.NewLine?+?Environment.NewLine?+?"{0}");var?codeGenerators?=?provider.GetServices<ICodeGenerator>();return?CreateServiceProviderCore(codeGenerators,?logger,?log4GenerateCode);}finally{(provider?as?IDisposable)?.Dispose();}}private?IServiceProvider?CreateServiceProviderCore(IEnumerable<ICodeGenerator>?codeGenerators,?ILogger?logger,?Action<ILogger,?string,?Exception>?log4GenerateCode){var?generatedTypes?=?new?List<GeneratedTypeEntry>();var?generationContext?=?new?CodeGenerationContext();generationContext.WriteLines("using?System;");generationContext.WriteLines("using?System.Reflection;");generationContext.WriteLines("using?System.Threading.Tasks;");generationContext.WriteLines("using?Microsoft.Extensions.DependencyInjection;");generationContext.WriteLines("");generationContext.WriteLines("namespace?Dora.Interception.CodeGeneration");using?(generationContext.CodeBlock()){foreach?(var?service?in?_services){foreach?(var?generator?in?codeGenerators){if?(generator.TryGenerate(service,?generationContext,?out?var?proxyTypeNames)){generatedTypes.Add(new?GeneratedTypeEntry(service,?proxyTypeNames!,?generator));break;}}}}log4GenerateCode(logger,?generationContext.SourceCode,?null!);if?(generatedTypes.Any()){var?compilation?=?CSharpCompilation.Create("Dora.Interception.CodeGeneration").WithOptions(new?CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,?optimizationLevel:?OptimizationLevel.Release)).AddSyntaxTrees(SyntaxFactory.ParseSyntaxTree(generationContext.SourceCode)).AddReferences(generationContext.References.Select(it?=>?MetadataReference.CreateFromFile(it.Location)));Assembly?outputAssembly;using?var?stream?=?new?MemoryStream();var?compilationResult?=?compilation.Emit(stream);if?(!compilationResult.Success){var?error?=?string.Join(Environment.NewLine,?compilationResult.Diagnostics);throw?new?InterceptionException($"It?fails?to?generate?proxy?class.?\n?{error}");}var?bytes?=?stream.ToArray();outputAssembly?=?Assembly.Load(bytes);foreach?(var?entry?in?generatedTypes){var?proxyTypes?=?entry.ProxyTypeNames.Select(it?=>?outputAssembly.GetType(it)).ToArray();entry.CodeGenerator.RegisterProxyType(_services,?entry.ServiceDescriptor,?proxyTypes!);}}_services.Replace(ServiceDescriptor.Singleton<IServiceLifetimeProvider>?(new?ServiceLifetimeProvider(_services)));var?serviceProvider?=?_services.BuildServiceProvider(_serviceProviderOptions);((ApplicationServicesAccessor)serviceProvider.GetRequiredService<IApplicationServicesAccessor>()).ApplicationServices?=?serviceProvider;MethodInvokerBuilder.Instance?=?serviceProvider.GetRequiredService<IMethodInvokerBuilder>();return?serviceProvider;}private?class?GeneratedTypeEntry{public?ServiceDescriptor?ServiceDescriptor?{?get;?}public?string[]?ProxyTypeNames?{?get;?}public?ICodeGenerator?CodeGenerator?{?get;?}public?GeneratedTypeEntry(ServiceDescriptor?serviceDescriptor,?string[]?proxyTypeNames,?ICodeGenerator?codeGenerator){ServiceDescriptor?=?serviceDescriptor;ProxyTypeNames?=?proxyTypeNames;CodeGenerator?=?codeGenerator;}}
}

前面廣泛使用的BuildInterceptableServiceProvider擴展方法定義如下,它直接使用了InterceptionBuilder對象來創建返回的IServiceProvider對象。

public?static?class?ServiceCollectionExtensions
{public?static?IServiceCollection?AddInterception(this?IServiceCollection?services,?Action<InterceptionBuilder>??setup?=?null);public?static?IServiceProvider?BuildInterceptableServiceProvider(this?IServiceCollection?services,?Action<InterceptionBuilder>??setup?=?null)=>?BuildInterceptableServiceProvider(services,?new?ServiceProviderOptions(),?setup);public?static?IServiceProvider?BuildInterceptableServiceProvider(this?IServiceCollection?services,?ServiceProviderOptions?serviceProviderOptions,?Action<InterceptionBuilder>??setup?=?null){Guard.ArgumentNotNull(services);Guard.ArgumentNotNull(serviceProviderOptions);var?factory?=?new?InterceptableServiceProviderFactory(serviceProviderOptions,?setup);var?builder?=?factory.CreateBuilder(services);return?builder.CreateServiceProvider();}
}public?sealed?class?InterceptionBuilder
{public?IServiceCollection?Services?{?get;?}
}

用于整合ASP.NET Core的UseInterception擴展方法定義如下,它注冊了上述的InterceptionServiceProviderFactory 類型,同時使用RequestServiceScopeFactory替換了默認的InvocationServiceScopeFactory,實現了將針對請求的IServiceProvider對象作為調用上下文的依賴注入容器。

public?static?class?HostBuilderExtensions
{public?static?IHostBuilder?UseInterception(this?IHostBuilder?hostBuilder,?Action<InterceptionBuilder>??setup?=?null)=>?UseInterception(hostBuilder,?new?ServiceProviderOptions(),?setup);public?static?IHostBuilder?UseInterception(this?IHostBuilder?hostBuilder,?ServiceProviderOptions?serviceProviderOptions,?Action<InterceptionBuilder>??setup?=?null){if?(hostBuilder?==?null)?throw?new?ArgumentNullException(nameof(hostBuilder));if?(serviceProviderOptions?==?null)?throw?new?ArgumentNullException(nameof(serviceProviderOptions));hostBuilder.ConfigureServices((_,?services)?=>?services.AddHttpContextAccessor());Action<InterceptionBuilder>?configure?=?builder?=>{builder.Services.Replace(ServiceDescriptor.Singleton<IInvocationServiceScopeFactory,?RequestServiceScopeFactory>());setup?.Invoke(builder);};return?hostBuilder.UseServiceProviderFactory(new?InterceptionServiceProviderFactory(serviceProviderOptions????new?ServiceProviderOptions(),?configure));}
}

八、看看生成的代理類

我們現在看看Dora.Interception生成的代理類型是攔截目標方法并執行注冊的攔截器的。目標類型或者方法是否為泛型、及方法的返回類型(Void、一般類型、Task、Value、Task<TResult>和Value<TResult>)以及是否包含ref/in/out參數都會影響最終生成的代理類型,所以這里我們之談論簡單的形式。我們先來看看針對接口的服務注冊最終會生成怎樣的代理類型。如下面的代碼片段所示,Foobar類型實現了IFoobar接口,對于實現的兩個方法,InvokeAsync方法上注冊了一個攔截器,Invoke方法則沒有。

var?foobar?=?new?ServiceCollection().AddSingleton<IFoobar,?Foobar>().BuildInterceptableServiceProvider().GetRequiredService<IFoobar>();public?interface?IFoobar
{Task?InvokeAsync(int?x,?string?y);void?Invoke(int?x,?int?y);
}public?class?Foobar?:?IFoobar
{[FakeInterceptor]public?virtual?Task?InvokeAsync(int?x,?string?y)?=>?throw?new?NotImplementedException();public?void?Invoke(int?x,?int?y)?=>?throw?new?NotImplementedException();
}

對于如上基于IFoobar/Foobar的服務注冊,最終會生成如下的代理類型FoobarProxy1479038137 (“1479038137”為隨機生成的確保命名不會重讀的后綴)。該類型實現了IFoobar接口,并利用封裝的Foobar對象來實現該接口的兩個方法。對于需要被攔截的InvokeAsync方法,會生成對應的方法調用上下文類型InvokeAsyncContext351732220,具體的實現方法上述的MethodInvokerBuilder對象生成與方法對應的調用管道來完成針對注冊攔截器和目標方法的調用。至于另一個不需要被攔截的Invoke方法,直接調用目標對象Foobar對應的方法即可。生成的FoobarProxy1479038137 類型的服務注冊將會覆蓋原來的服務注冊。

using?Microsoft.Extensions.DependencyInjection;
using?System.Reflection;namespace?Dora.Interception.CodeGeneration
{public?class?FoobarProxy1479038137?:?App.IFoobar,?IInterfaceProxy{private?readonly?App.IFoobar?_target;private?readonly?IInvocationServiceScopeFactory?_scopeFactory;private?static?readonly?Lazy<MethodInfo>?_methodOfInvokeAsync607503395?=?new?Lazy<MethodInfo>(()?=>?ProxyHelper.GetMethodInfo<App.Foobar>(100663305));private?static?readonly?Lazy<InvokeDelegate>?_invokerOfInvokeAsync951608024?=?new?Lazy<InvokeDelegate>(()?=>?MethodInvokerBuilder.Instance.Build(typeof(App.Foobar),?ProxyHelper.GetMethodInfo<App.Foobar>(100663305),?InvokeAsync791452913));public?FoobarProxy1479038137(IServiceProvider?provider,?IInvocationServiceScopeFactory?scopeFactory){_target?=?ActivatorUtilities.CreateInstance<App.Foobar>(provider);_scopeFactory?=?scopeFactory;}public?System.Threading.Tasks.Task?InvokeAsync(System.Int32?x,?System.String?y){using?var?scope?=?_scopeFactory.CreateInvocationScope();var?method?=?_methodOfInvokeAsync607503395.Value;var?context?=?new?InvokeAsyncContext351732220(_target,?x,?y,?method,?scope.ServiceProvider);var?valueTask?=?_invokerOfInvokeAsync951608024.Value.Invoke(context);return?valueTask.AsTask();}public?static?ValueTask?InvokeAsync791452913(InvocationContext?invocationContext){var?context?=?(InvokeAsyncContext351732220)invocationContext;var?target?=?(App.IFoobar)invocationContext.Target;var?returnValue?=?target.InvokeAsync(context._x,?context._y);context._returnValue?=?returnValue;return?new?ValueTask(returnValue);}public?void?Invoke(System.Int32?x,?System.Int32?y)=>?_target.Invoke(x,?y);private?class?InvokeAsyncContext351732220?:?InvocationContext{internal?System.Int32?_x;internal?System.String?_y;internal?System.Threading.Tasks.Task?_returnValue;public?override?MethodInfo?MethodInfo?{?get;?}public?override?IServiceProvider?InvocationServices?{?get;?}public?InvokeAsyncContext351732220(object?target,?System.Int32?x,?System.String?y,?MethodInfo?method,?IServiceProvider?invocationServices)?:?base(target){_x?=?x;_y?=?y;MethodInfo?=?method;InvocationServices?=?invocationServices;}public?override?TArgument?GetArgument<TArgument>(string?name){return?name?switch{"x"?=>?ProxyHelper.GetArgumentOrReturnValue<System.Int32,?TArgument>(_x),"y"?=>?ProxyHelper.GetArgumentOrReturnValue<System.String,?TArgument>(_y),_?=>?throw?new?ArgumentException($"Invalid?argument?name?{name}.",?nameof(name))};}public?override?TArgument?GetArgument<TArgument>(int?index){return?index?switch{0?=>?ProxyHelper.GetArgumentOrReturnValue<System.Int32,?TArgument>(_x),1?=>?ProxyHelper.GetArgumentOrReturnValue<System.String,?TArgument>(_y),_?=>?throw?new?ArgumentOutOfRangeException(nameof(index))};}public?override?InvocationContext?SetArgument<TArgument>(string?name,?TArgument?value){return?name?switch{"x"?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeAsyncContext351732220,?System.Int32,?TArgument>(this,?value,?(ctx,?val)?=>?ctx._x?=?val),"y"?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeAsyncContext351732220,?System.String,?TArgument>(this,?value,?(ctx,?val)?=>?ctx._y?=?val),_?=>?throw?new?ArgumentException($"Invalid?argument?name?{name}.",?nameof(name))};}public?override?InvocationContext?SetArgument<TArgument>(int?index,?TArgument?value){return?index?switch{0?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeAsyncContext351732220,?System.Int32,?TArgument>(this,?value,?(ctx,?val)?=>?ctx._x?=?val),1?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeAsyncContext351732220,?System.String,?TArgument>(this,?value,?(ctx,?val)?=>?ctx._y?=?val),_?=>?throw?new?ArgumentOutOfRangeException(nameof(index))};}public?override?TReturnValue?GetReturnValue<TReturnValue>()?=>?ProxyHelper.GetArgumentOrReturnValue<System.Threading.Tasks.Task,?TReturnValue>(_returnValue);public?override?InvocationContext?SetReturnValue<TReturnValue>(TReturnValue?value)?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeAsyncContext351732220,?System.Threading.Tasks.Task,?TReturnValue>(this,?value,?(ctx,?val)?=>?ctx._returnValue?=?val);}}
}

現在我們將針對接口的服務注冊替換成基于Foobar類型自身的注冊。

var?foobar?=?new?ServiceCollection().AddSingleton<?Foobar>().AddLogging(logging=>logging.AddConsole()).BuildInterceptableServiceProvider().GetRequiredService<Foobar>();

這次會生成如下所示的代理類FoobarProxy1224512711 。該類型將Foobar作為基類,通過重寫InvokeAsync方法完成針對攔截器和目標方法的執行。至于不需要被攔截的Invoke方法則不需要考慮。最終針對Foobar/FoobarProxy1224512711的服務注冊將用來替換掉現有針對Foobar的服務注冊。

using?System.Reflection;namespace?Dora.Interception.CodeGeneration
{public?sealed?class?FoobarProxy1224512711?:?App.Foobar,?IVirtualMethodProxy{private?readonly?IInvocationServiceScopeFactory?_scopeFactory;private?static?readonly?Lazy<MethodInfo>?_methodOfInvokeAsync1812877040?=?new?Lazy<MethodInfo>(()?=>?ProxyHelper.GetMethodInfo<App.Foobar>(100663305));private?readonly?Lazy<InvokeDelegate>?_invokerOfInvokeAsync11225071;public?FoobarProxy1224512711(IInvocationServiceScopeFactory?scopeFactory)?:?base(){_scopeFactory?=?scopeFactory;_invokerOfInvokeAsync11225071?=?new?Lazy<InvokeDelegate>(()?=>?MethodInvokerBuilder.Instance.Build(typeof(App.Foobar),?ProxyHelper.GetMethodInfo<App.Foobar>(100663305),?InvokeAsync243545362));}public?override?System.Threading.Tasks.Task?InvokeAsync(System.Int32?x,?System.String?y){using?var?scope?=?_scopeFactory.CreateInvocationScope();var?method?=?_methodOfInvokeAsync1812877040.Value;var?context?=?new?InvokeAsyncContext1151816636(this,?x,?y,?method,?scope.ServiceProvider);var?valueTask?=?_invokerOfInvokeAsync11225071.Value.Invoke(context);return?valueTask.AsTask();}public?ValueTask?InvokeAsync243545362(InvocationContext?invocationContext){var?context?=?(InvokeAsyncContext1151816636)invocationContext;var?returnValue?=?base.InvokeAsync(context._x,?context._y);context._returnValue?=?returnValue;return?new?ValueTask(returnValue);}private?class?InvokeAsyncContext1151816636?:?InvocationContext{internal?System.Int32?_x;internal?System.String?_y;internal?System.Threading.Tasks.Task?_returnValue;public?override?MethodInfo?MethodInfo?{?get;?}public?override?IServiceProvider?InvocationServices?{?get;?}public?InvokeAsyncContext1151816636(object?target,?System.Int32?x,?System.String?y,?MethodInfo?method,?IServiceProvider?invocationServices)?:?base(target){_x?=?x;_y?=?y;MethodInfo?=?method;InvocationServices?=?invocationServices;}public?override?TArgument?GetArgument<TArgument>(string?name){return?name?switch{"x"?=>?ProxyHelper.GetArgumentOrReturnValue<System.Int32,?TArgument>(_x),"y"?=>?ProxyHelper.GetArgumentOrReturnValue<System.String,?TArgument>(_y),_?=>?throw?new?ArgumentException($"Invalid?argument?name?{name}.",?nameof(name))};}public?override?TArgument?GetArgument<TArgument>(int?index){return?index?switch{0?=>?ProxyHelper.GetArgumentOrReturnValue<System.Int32,?TArgument>(_x),1?=>?ProxyHelper.GetArgumentOrReturnValue<System.String,?TArgument>(_y),_?=>?throw?new?ArgumentOutOfRangeException(nameof(index))};}public?override?InvocationContext?SetArgument<TArgument>(string?name,?TArgument?value){return?name?switch{"x"?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeAsyncContext1151816636,?System.Int32,?TArgument>(this,?value,?(ctx,?val)?=>?ctx._x?=?val),"y"?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeAsyncContext1151816636,?System.String,?TArgument>(this,?value,?(ctx,?val)?=>?ctx._y?=?val),_?=>?throw?new?ArgumentException($"Invalid?argument?name?{name}.",?nameof(name))};}public?override?InvocationContext?SetArgument<TArgument>(int?index,?TArgument?value){return?index?switch{0?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeAsyncContext1151816636,?System.Int32,?TArgument>(this,?value,?(ctx,?val)?=>?ctx._x?=?val),1?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeAsyncContext1151816636,?System.String,?TArgument>(this,?value,?(ctx,?val)?=>?ctx._y?=?val),_?=>?throw?new?ArgumentOutOfRangeException(nameof(index))};}public?override?TReturnValue?GetReturnValue<TReturnValue>()?=>?ProxyHelper.GetArgumentOrReturnValue<System.Threading.Tasks.Task,?TReturnValue>(_returnValue);public?override?InvocationContext?SetReturnValue<TReturnValue>(TReturnValue?value)?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeAsyncContext1151816636,?System.Threading.Tasks.Task,?TReturnValue>(this,?value,?(ctx,?val)?=>?ctx._returnValue?=?val);}}
}

對于針對接口的服務注冊,如果攔截器被應用的方法并沒有定義定義在該接口中,此時會綜合應用上述兩種代碼生成方案。為了演示我們對IFoobar和Foobar類型做了如下的修改。

public?interface?IFoobar
{Task?InvokeAsync(int?x,?string?y);
}[FakeInterceptor]
public?class?Foobar?:?IFoobar
{public?virtual?Task?InvokeAsync(int?x,?string?y)?=>?throw?new?NotImplementedException();public?virtual?void?Invoke(int?x,?int?y)?=>?throw?new?NotImplementedException();
}

由于Foobar的兩個方法都注冊了攔截器,但是Invoke屬于Foobar獨有的方法,此時會生成如下兩個代理類FoobarProxy1796625286和FoobarProxy1493741432 ,前者繼承Foobar類型,后者實現IFoobar接口。基于IFoobar/FoobarProxy1493741432 的服務注冊會用來替換現有的服務注冊。

public?sealed?class?FoobarProxy1796625286?:?App.Foobar,?IVirtualMethodProxy
{private?readonly?IInvocationServiceScopeFactory?_scopeFactory;private?static?readonly?Lazy<MethodInfo>?_methodOfInvoke717038218?=?new?Lazy<MethodInfo>(()?=>?ProxyHelper.GetMethodInfo<App.Foobar>(100663305));private?readonly?Lazy<InvokeDelegate>?_invokerOfInvoke1480842411;public?FoobarProxy1796625286(IInvocationServiceScopeFactory?scopeFactory)?:?base(){_scopeFactory?=?scopeFactory;_invokerOfInvoke1480842411?=?new?Lazy<InvokeDelegate>(()?=>?MethodInvokerBuilder.Instance.Build(typeof(App.Foobar),?ProxyHelper.GetMethodInfo<App.Foobar>(100663305),?Invoke880402619));}public?override?void?Invoke(System.Int32?x,?System.Int32?y){using?var?scope?=?_scopeFactory.CreateInvocationScope();var?method?=?_methodOfInvoke717038218.Value;var?context?=?new?InvokeContext307659875(this,?x,?y,?method,?scope.ServiceProvider);var?valueTask?=?_invokerOfInvoke1480842411.Value.Invoke(context);valueTask.GetAwaiter().GetResult();}public?ValueTask?Invoke880402619(InvocationContext?invocationContext){var?context?=?(InvokeContext307659875)invocationContext;base.Invoke(context._x,?context._y);return?ValueTask.CompletedTask;}private?class?InvokeContext307659875?:?InvocationContext{internal?System.Int32?_x;internal?System.Int32?_y;public?override?MethodInfo?MethodInfo?{?get;?}public?override?IServiceProvider?InvocationServices?{?get;?}public?InvokeContext307659875(object?target,?System.Int32?x,?System.Int32?y,?MethodInfo?method,?IServiceProvider?invocationServices)?:?base(target){_x?=?x;_y?=?y;MethodInfo?=?method;InvocationServices?=?invocationServices;}public?override?TArgument?GetArgument<TArgument>(string?name){return?name?switch{"x"?=>?ProxyHelper.GetArgumentOrReturnValue<System.Int32,?TArgument>(_x),"y"?=>?ProxyHelper.GetArgumentOrReturnValue<System.Int32,?TArgument>(_y),_?=>?throw?new?ArgumentException($"Invalid?argument?name?{name}.",?nameof(name))};}public?override?TArgument?GetArgument<TArgument>(int?index){return?index?switch{0?=>?ProxyHelper.GetArgumentOrReturnValue<System.Int32,?TArgument>(_x),1?=>?ProxyHelper.GetArgumentOrReturnValue<System.Int32,?TArgument>(_y),_?=>?throw?new?ArgumentOutOfRangeException(nameof(index))};}public?override?InvocationContext?SetArgument<TArgument>(string?name,?TArgument?value){return?name?switch{"x"?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeContext307659875,?System.Int32,?TArgument>(this,?value,?(ctx,?val)?=>?ctx._x?=?val),"y"?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeContext307659875,?System.Int32,?TArgument>(this,?value,?(ctx,?val)?=>?ctx._y?=?val),_?=>?throw?new?ArgumentException($"Invalid?argument?name?{name}.",?nameof(name))};}public?override?InvocationContext?SetArgument<TArgument>(int?index,?TArgument?value){return?index?switch{0?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeContext307659875,?System.Int32,?TArgument>(this,?value,?(ctx,?val)?=>?ctx._x?=?val),1?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeContext307659875,?System.Int32,?TArgument>(this,?value,?(ctx,?val)?=>?ctx._y?=?val),_?=>?throw?new?ArgumentOutOfRangeException(nameof(index))};}public?override?TReturnValue?GetReturnValue<TReturnValue>()?=>?default;public?override?InvocationContext?SetReturnValue<TReturnValue>(TReturnValue?value)?=>?this;}
}
public?class?FoobarProxy1493741432?:?App.IFoobar,?IInterfaceProxy
{private?readonly?App.IFoobar?_target;private?readonly?IInvocationServiceScopeFactory?_scopeFactory;private?static?readonly?Lazy<MethodInfo>?_methodOfInvokeAsync209693810?=?new?Lazy<MethodInfo>(()?=>?ProxyHelper.GetMethodInfo<App.Foobar>(100663304));private?static?readonly?Lazy<InvokeDelegate>?_invokerOfInvokeAsync2048425446?=?new?Lazy<InvokeDelegate>(()?=>?MethodInvokerBuilder.Instance.Build(typeof(App.Foobar),?ProxyHelper.GetMethodInfo<App.Foobar>(100663304),?InvokeAsync1286715673));public?FoobarProxy1493741432(IServiceProvider?provider,?IInvocationServiceScopeFactory?scopeFactory){_target?=?ActivatorUtilities.CreateInstance<Dora.Interception.CodeGeneration.FoobarProxy1796625286>(provider);_scopeFactory?=?scopeFactory;}public?System.Threading.Tasks.Task?InvokeAsync(System.Int32?x,?System.String?y){using?var?scope?=?_scopeFactory.CreateInvocationScope();var?method?=?_methodOfInvokeAsync209693810.Value;var?context?=?new?InvokeAsyncContext1177601686(_target,?x,?y,?method,?scope.ServiceProvider);var?valueTask?=?_invokerOfInvokeAsync2048425446.Value.Invoke(context);return?valueTask.AsTask();}public?static?ValueTask?InvokeAsync1286715673(InvocationContext?invocationContext){var?context?=?(InvokeAsyncContext1177601686)invocationContext;var?target?=?(App.IFoobar)invocationContext.Target;var?returnValue?=?target.InvokeAsync(context._x,?context._y);context._returnValue?=?returnValue;return?new?ValueTask(returnValue);}private?class?InvokeAsyncContext1177601686?:?InvocationContext{internal?System.Int32?_x;internal?System.String?_y;internal?System.Threading.Tasks.Task?_returnValue;public?override?MethodInfo?MethodInfo?{?get;?}public?override?IServiceProvider?InvocationServices?{?get;?}public?InvokeAsyncContext1177601686(object?target,?System.Int32?x,?System.String?y,?MethodInfo?method,?IServiceProvider?invocationServices)?:?base(target){_x?=?x;_y?=?y;MethodInfo?=?method;InvocationServices?=?invocationServices;}public?override?TArgument?GetArgument<TArgument>(string?name){return?name?switch{"x"?=>?ProxyHelper.GetArgumentOrReturnValue<System.Int32,?TArgument>(_x),"y"?=>?ProxyHelper.GetArgumentOrReturnValue<System.String,?TArgument>(_y),_?=>?throw?new?ArgumentException($"Invalid?argument?name?{name}.",?nameof(name))};}public?override?TArgument?GetArgument<TArgument>(int?index){return?index?switch{0?=>?ProxyHelper.GetArgumentOrReturnValue<System.Int32,?TArgument>(_x),1?=>?ProxyHelper.GetArgumentOrReturnValue<System.String,?TArgument>(_y),_?=>?throw?new?ArgumentOutOfRangeException(nameof(index))};}public?override?InvocationContext?SetArgument<TArgument>(string?name,?TArgument?value){return?name?switch{"x"?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeAsyncContext1177601686,?System.Int32,?TArgument>(this,?value,?(ctx,?val)?=>?ctx._x?=?val),"y"?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeAsyncContext1177601686,?System.String,?TArgument>(this,?value,?(ctx,?val)?=>?ctx._y?=?val),_?=>?throw?new?ArgumentException($"Invalid?argument?name?{name}.",?nameof(name))};}public?override?InvocationContext?SetArgument<TArgument>(int?index,?TArgument?value){return?index?switch{0?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeAsyncContext1177601686,?System.Int32,?TArgument>(this,?value,?(ctx,?val)?=>?ctx._x?=?val),1?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeAsyncContext1177601686,?System.String,?TArgument>(this,?value,?(ctx,?val)?=>?ctx._y?=?val),_?=>?throw?new?ArgumentOutOfRangeException(nameof(index))};}public?override?TReturnValue?GetReturnValue<TReturnValue>()?=>?ProxyHelper.GetArgumentOrReturnValue<System.Threading.Tasks.Task,?TReturnValue>(_returnValue);public?override?InvocationContext?SetReturnValue<TReturnValue>(TReturnValue?value)?=>?ProxyHelper.SetArgumentOrReturnValue<InvokeAsyncContext1177601686,?System.Threading.Tasks.Task,?TReturnValue>(this,?value,?(ctx,?val)?=>?ctx._returnValue?=?val);}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/281943.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281943.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281943.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

activemq 安全連接

一、定義用戶組1.1 simpleAuthenticationPlugin通過在activemq.xml中配置用戶組<plugins> <simpleAuthenticationPlugin> <users> <authenticationUser username"admin" password"password" groups"admins,publishers,consumer…

React Native在Android當中實踐(五)——常見問題

React Native在Android當中實踐&#xff08;一&#xff09;——背景介紹 React Native在Android當中實踐&#xff08;二&#xff09;——搭建開發環境 React Native在Android當中實踐&#xff08;三&#xff09;——集成到Android項目當中 React Native在Android當中實踐&#…

完成登錄與注冊頁面的前端

完成登錄與注冊頁面的HTMLCSSJS&#xff0c;其中的輸入項檢查包括&#xff1a; 用戶名6-12位 首字母不能是數字 只能包含字母和數字 密碼6-12位 注冊頁兩次密碼是否一致 JS&#xff1a; function fnLogin() {var uSer document.getElementById("user");var pAss do…

mysql505復位密碼_mysql5 如何復位根用戶密碼[官方文檔]

如何復位根用戶密碼如果你從未為MySQL設置根用戶密碼&#xff0c;服務器在以根用戶身份進行連接時不需要密碼。但是&#xff0c;建議你為每個賬戶設置密碼如果你以前設置了根用戶密碼&#xff0c;但卻忘記了該密碼&#xff0c;可設置新的密碼。下述步驟是針對Windows平臺的。在…

WPF效果第二百零一篇之實現合并單元格

早一段時間又一次出差青海省西寧市;回來又是總結又是各種瑣事,也沒顧得上去分享點東西;大周末的就在家分享一下,這二天再次基于ListBox實現的合并單元格的效果:1、ListBox嵌套ListBox的前臺布局:<ListBox ItemsSource"{Binding LCPListData}" x:Name"Manufac…

轉載 maven 詳解 http://www.cnblogs.com/binyue/p/4729134.html

--聲明規范 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <!--聲…

ASP.NET Core中使用EasyCaching作為緩存抽象層

簡介做后端開發&#xff0c;緩存應該是天天在用&#xff0c;很多時候我們的做法是寫個幫助類&#xff0c;然后用到的時候調用一下。這種只適合簡單層次的應用&#xff1b;一旦涉及到接口實現調整之類的&#xff0c;這種強耦合的做法很不合適。有些其他的功能又要去重復造輪子。…

mysql qps如何查看_mysql狀態查看 QPS/TPS/緩存命中率查看

運行中的mysql狀態查看對正在運行的mysql進行監控&#xff0c;其中一個方式就是查看mysql運行狀態。(1)QPS(每秒Query量)QPS Questions(or Queries) / uptimemysql > show global status like Question%;mysql > show global status like uptime%;(2)TPS(每秒事務量…

visual studio開啟多核編譯方法

先按http://blog.csdn.net/acaiwlj/article/details/50240625的方法進行了VS多線程的啟動。 原本以為按以下步驟設置就OK了&#xff0c;但是編譯中無意間發些了一個warning&#xff1a;“/Gm”與多處理不兼容&#xff1b;忽略 /MP 開關&#xff01;&#xff01;&#xff01;&am…

聊聊storm nimbus的LeaderElector

為什么80%的碼農都做不了架構師&#xff1f;>>> 序 本文主要研究一下storm nimbus的LeaderElector Nimbus org/apache/storm/daemon/nimbus/Nimbus.java public static void main(String[] args) throws Exception {Utils.setupDefaultUncaughtExceptionHandler();…

Android框架式編程之BufferKnife

BufferKnife作為框架式編程的重要組成部分&#xff0c;使用BufferKnife能夠極大的精簡View層面的代碼量&#xff0c;并為MVP/MVC方式提供輔助。 一、配置 compile com.jakewharton:butterknife:(insert latest version) annotationProcessor com.jakewharton:butterknife-compi…

如果我去深圳,你會見我嗎

▲圖/ 深圳夜景初次見易小姐&#xff0c;還是21年的春節回老家的時候。想來20年因為疫情沒有回家&#xff0c;家母幾次三番電話里頭表達的思念以及建議一些不靠譜的回家計劃&#xff0c;著實有些不忍&#xff0c;確實有似“兒行千里母擔憂”之理&#xff0c;索性拿著年假和加班…

CodeForces - 1059D(二分+誤差)

鏈接&#xff1a;CodeForces - 1059D 題意&#xff1a;給出笛卡爾坐標系上 n 個點&#xff0c;求與 x 軸相切且覆蓋了所有給出點的圓的最小半徑。 題解&#xff1a;二分半徑即可。判斷&#xff1a;假設當前二分到的半徑是 R &#xff0c;因為要和 x 軸相切&#xff0c;所以圓心…

pureref 平移用不了_關于參考圖管理神器 PureRef 的一些快捷鍵

PureRef 的一些快捷鍵 軟件下載&#xff1a;點擊這里控制(配合左鍵)窗口內鼠標左鍵     框選窗口邊鼠標左鍵     調整窗口大小鼠標中鍵 或 按住Alt     移動畫布鼠標滾輪 或 按住Z     縮放畫布按住S     查看目標位置顏色信息(可復制16進制顏色…

Windows 10 版本信息

Windows 10 版本信息 原文 https://technet.microsoft.com/zh-cn/windows/release-info Windows 10 版本信息 Microsoft 已更新其服務模型。 半年頻道每年發布兩次功能更新&#xff0c;時間大概在 3 月和 9 月&#xff0c;每個版本的服務時間線為 18 個月。 從 Windows 10 版本…

開源輕量的 .NET 監控工具 - 看門狗

你好&#xff0c;這里是 Dotnet 工具箱&#xff0c;定期分享 Dotnet 有趣&#xff0c;實用的工具或組件&#xff0c;希望對您有用&#xff01;簡介WatchDog 是一個使用 C# 開發的開源的輕量監控工具&#xff0c;它可以記錄和查看 ASP.Net Core Web 和 WebApi 的實時消息、事件、…

python讀取oracle數據庫性能_用python對oracle進行簡單性能測試

一、概述dba在工作中避不開的兩個問題&#xff0c;sql使用綁定變量到底會有多少的性能提升&#xff1f;數據庫的審計功能如果打開對數據庫的性能會產生多大的影響&#xff1f;最近恰好都碰到了&#xff0c;索性做個實驗。sql使用綁定變量對性能的影響開通數據庫審計功能對性能的…

BZOJ 3231: [Sdoi2008]遞歸數列 (JZYZOJ 1353) 矩陣快速冪

http://www.lydsy.com/JudgeOnline/problem.php?id3231和斐波那契一個道理在最后加一個求和即可1 #include<cstdio>2 #include<cstring>3 #include<iostream>4 //using namespace std;5 const int maxn10010;6 const double eps1e-8;7 long long modn;8 lon…

馬斯克的火箭上天了,SpaceX開源項目也登上了熱榜!

python知識手冊SpaceX于美國東部時間5月30日下午3&#xff1a;22分將兩位美國宇航員送往國際空間站&#xff0c;雖然這只是Demo任務&#xff0c;但SpaceX已經以其卓越工程優勢、低廉的發射成本贏得了全球航天產業的信賴。同時也是除美俄中這些航天國家隊以外&#xff0c;唯一獨…

EasyMock學習筆記

目前在接觸平臺側的開發&#xff0c;發現平臺側的東西和以前javacard開發很不一樣&#xff0c;看來以后要學的東西還有很多很多。今天接觸了下EasyMock。 Mock 方法是單元測試中常見的一種技術&#xff0c;它的主要作用是模擬一些在應用中不容易構造或者比較復雜的對象&#xf…