日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

[ASP.NET Core MVC] 如何实现运行时动态定义Controller类型?

發布時間:2023/12/4 asp.net 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [ASP.NET Core MVC] 如何实现运行时动态定义Controller类型? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

昨天有個朋友在微信上問我一個問題:他希望通過動態腳本的形式實現對ASP.NET Core MVC應用的擴展,比如在程序運行過程中上傳一段C#腳本將其中定義的Controller類型注冊到應用中,問我是否有好解決方案。這是一個挺有意思的問題,我們可以通過兩種方案實現了這個需求。

01

實現效果

我們先來看看實現的效果。如下所示的是一個MVC應用的主頁,我們可以在文本框中通過編寫C#代碼定義一個有效的Controller類型,然后點擊“Register”按鈕,定義的Controller類型將自動注冊到MVC應用中

由于我們采用了針對模板為“{controller}/{action}”的約定路由,所以我們采用路徑“/foo/bar”就可以訪問上圖中定義在FooController中的Action方法Bar,下圖證實了這一點。

02

動態編譯源代碼

要實現如上所示的“針對Controller類型的動態注冊”,首先需要解決的是針對提供源代碼的動態編譯問題,我們知道這個可以利用Roslyn來解決。具體來說,我們定義了如下這個ICompiler接口,它的Compile方法將會對參數sourceCode提供的源代碼進行編譯。該方法返回源代碼動態編譯生成的程序集,它的第二個參數代表引用的程序集。

public?interface?ICompiler {Assembly?Compile(string?text,?params?Assembly[]?referencedAssemblies); }

如下所示的Compiler類型是對ICompiler接口的默認實現。

public?class?Compiler?:?ICompiler {public?Assembly?Compile(string?text,?params?Assembly[]?referencedAssemblies){var?references?=?referencedAssemblies.Select(it?=>?MetadataReference.CreateFromFile(it.Location));var?options?=?new?CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);var?assemblyName?=?"_"?+?Guid.NewGuid().ToString("D");var?syntaxTrees?=?new?SyntaxTree[]?{?CSharpSyntaxTree.ParseText(text)?};var?compilation?=?CSharpCompilation.Create(assemblyName,?syntaxTrees,?references,?options);using?var?stream?=?new?MemoryStream();var?compilationResult?=?compilation.Emit(stream);if?(compilationResult.Success){stream.Seek(0,?SeekOrigin.Begin);return?Assembly.Load(stream.ToArray());}throw?new?InvalidOperationException("Compilation?error");} }

03

DynamicActionProvider?

解決了針對提供源代碼的動態編譯問題之后,我們可以獲得需要注冊的Controller類型,那么如何將它注冊MVC應用上呢?要回答這個問題,我們得對MVC框架的執行原理有一個大致的了解:ASP.NET Core通過一個由服務器和若干中間件構成的管道來處理請求,MVC框架建立在通過EndpointRoutingMiddleware和EndpointMiddleare這兩個中間件構成的終結點路由系統上。此路由系統維護著一組路由終結點,該終結點體現為一個路由模式(Route Pattern)與對應處理器(通過RequestDelegate委托表示)之間的映射。

由于針對MVC應用的請求總是指向某一個Action,所以MVC框架提供的路由整合機制體現在為每一個Action創建一個或者多個終結點(同一個Action方法可以注冊多個路由)。針對Action方法的路由終結點是根據描述Action方法的ActionDescriptor對象構建而成的。至于ActionDescriptor對象,則是通過注冊的一組IActionDescriptorProvider對象來提供的,那么我們的問題就迎刃而解:通過注冊自定義的IActionDescriptorProvider從動態定義的Controller類型中解析出合法的Action方法,并創建對應的ActionDescriptor對象即可。

那么ActionDescriptor如何創建呢?我們能想到簡單的方式是調用如下這個Build方法。針對該方法的調用存在兩個問題:第一,ControllerActionDescriptorBuilder是一個內部(internal)類型,我們指定以反射的方式調用這個方法,第二,這個方法接受一個類型為ApplicationModel的參數。

internal?static?class?ControllerActionDescriptorBuilder {public?static?IList<ControllerActionDescriptor>?Build(ApplicationModel?application); }

ApplicationModel類型涉及到一個很大的主題:MVC應用模型,目前我們現在只關注如何創建這個對象。表示MVC應用模型的ApplicationModel對象是通過對應的工廠ApplicationModelFactory創建的。這個工廠會自動注冊到MVC應用的依賴注入框架中,但是這依然是一個內部(內部)類型,所以還得反射。

internal?class?ApplicationModelFactory {public?ApplicationModel?CreateApplicationModel(IEnumerable<TypeInfo>?controllerTypes); }

我們定義了如下這個DynamicActionProvider類型實現了IActionDescriptorProvider接口。針對提供的源代碼向ActionDescriptor列表的轉換體現在AddControllers方法中:它利用ICompiler對象編譯源代碼,并在生成的程序集中解析出有效的Controller類型,然后利用ApplicationModelFactory創建出代表應用模型的ApplicationModel對象,后者作為參數調用ControllerActionDescriptorBuilder的靜態方法Build創建出描述所有Action方法的ActionDescriptor對象。

public?class?DynamicActionProvider?:?IActionDescriptorProvider {private?readonly?List<ControllerActionDescriptor>?_actions;private?readonly?Func<string,?IEnumerable<ControllerActionDescriptor>>?_creator;public?DynamicActionProvider(IServiceProvider?serviceProvider,?ICompiler?compiler){_actions?=?new?List<ControllerActionDescriptor>();_creator?=?CreateActionDescrptors;IEnumerable<ControllerActionDescriptor>?CreateActionDescrptors(string?sourceCode){var?assembly?=?compiler.Compile(sourceCode,?Assembly.Load(new?AssemblyName("System.Runtime")),typeof(object).Assembly,typeof(ControllerBase).Assembly,typeof(Controller).Assembly);var?controllerTypes?=?assembly.GetTypes().Where(it?=>?IsController(it));var?applicationModel?=?CreateApplicationModel(controllerTypes);assembly?=?Assembly.Load(new?AssemblyName("Microsoft.AspNetCore.Mvc.Core"));var?typeName?=?"Microsoft.AspNetCore.Mvc.ApplicationModels.ControllerActionDescriptorBuilder";var?controllerBuilderType?=?assembly.GetTypes().Single(it?=>?it.FullName?==?typeName);var?buildMethod?=?controllerBuilderType.GetMethod("Build",?BindingFlags.Static?|?BindingFlags.Public);return?(IEnumerable<ControllerActionDescriptor>)buildMethod.Invoke(null,?new?object[]?{?applicationModel?});}ApplicationModel?CreateApplicationModel(IEnumerable<Type>?controllerTypes){var?assembly?=?Assembly.Load(new?AssemblyName("Microsoft.AspNetCore.Mvc.Core"));var?typeName?=?"Microsoft.AspNetCore.Mvc.ApplicationModels.ApplicationModelFactory";var?factoryType?=?assembly.GetTypes().Single(it?=>?it.FullName?==?typeName);var?factory?=?serviceProvider.GetService(factoryType);var?method?=?factoryType.GetMethod("CreateApplicationModel");var?typeInfos?=?controllerTypes.Select(it?=>?it.GetTypeInfo());return?(ApplicationModel)method.Invoke(factory,?new?object[]?{?typeInfos?});}bool?IsController(Type?typeInfo){if?(!typeInfo.IsClass)?return?false;if?(typeInfo.IsAbstract)?return?false;if?(!typeInfo.IsPublic)?return?false;if?(typeInfo.ContainsGenericParameters)?return?false;if?(typeInfo.IsDefined(typeof(NonControllerAttribute)))?return?false;if?(!typeInfo.Name.EndsWith("Controller",?StringComparison.OrdinalIgnoreCase)?&&?!typeInfo.IsDefined(typeof(ControllerAttribute)))?return?false;return?true;}}public?int?Order?=>?-100;public?void?OnProvidersExecuted(ActionDescriptorProviderContext?context)?{?}public?void?OnProvidersExecuting(ActionDescriptorProviderContext?context){foreach?(var?action?in?_actions){context.Results.Add(action);}}public?void?AddControllers(string?sourceCode)?=>?_actions.AddRange(_creator(sourceCode)); }

總結

以上是生活随笔為你收集整理的[ASP.NET Core MVC] 如何实现运行时动态定义Controller类型?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。