C# 添加在运行时可用于Razor页面的程序集/类型
我正在尝试构建一个动态Web界面,在该界面中,我可以动态地指向一个文件夹,并使用ASP.NET核心从该文件夹中提供Web内容。通过使用ASP.NET Core中的文件提供程序来重新路由Web根文件夹,这非常容易。这适用于静态文件和RazorPages 然而,对于RazorPages来说,问题是一旦您这样做了,就不能为其他类型动态添加引用。我希望能够有选择地添加一个文件夹(PrivateBin),在启动时我可以循环使用它,加载程序集,然后让这些程序集在Razor中可见 不幸的是,它无法工作,因为Razor即使在使用运行时编译时也看不到加载的程序集 我在启动期间使用以下命令加载程序集。请注意,从中加载这些内容的文件夹不在默认ContentRoot或WebRoot中,而是在新的重定向WebRoot中C# 添加在运行时可用于Razor页面的程序集/类型,c#,razor-pages,asp.net-core-3.0,C#,Razor Pages,Asp.net Core 3.0,我正在尝试构建一个动态Web界面,在该界面中,我可以动态地指向一个文件夹,并使用ASP.NET核心从该文件夹中提供Web内容。通过使用ASP.NET Core中的文件提供程序来重新路由Web根文件夹,这非常容易。这适用于静态文件和RazorPages 然而,对于RazorPages来说,问题是一旦您这样做了,就不能为其他类型动态添加引用。我希望能够有选择地添加一个文件夹(PrivateBin),在启动时我可以循环使用它,加载程序集,然后让这些程序集在Razor中可见 不幸的是,它无法工作,因为R
//WebRoot是用户选择的路径,在这里通过命令行--WebRoot c:\temp\web指定
私有void加载私有二进制程序集()
{
var binPath=Path.Combine(WebRoot,“PrivateBin”);
if(Directory.Exists(binPath))
{
var files=Directory.GetFiles(binPath);
foreach(文件中的var文件)
{
如果(!file.EndsWith(“.dll”,StringComparison.CurrentCultureIgnoreCase)&&
!file.EndsWith(“.exe”,StringComparison.InvariantCultureIgnoreCase))
继续;
尝试
{
var asm=AssemblyLoadContext.Default.LoadFromAssemblyPath(文件);
Console.WriteLine(“附加程序集:+文件”);
}
捕获(例外情况除外)
{
Console.WriteLine(“未能加载专用程序集:+文件”);
}
}
}
}
程序集加载到AssemblyLoadContext()中,我可以使用反射和Type.GetType(“namespace.class,assembly”)
访问该类型
但是,当我尝试访问RazorPages中的类型时(即使启用了运行时编译),这些类型也不可用。我得到以下错误:
为了确保该类型确实可用,我检查了Razor内部是否可以执行以下操作:
@{
var md=Type.GetType(“Westwind.AspNetCore.Markdown.Markdown,Westwind.AspNetCore.Markdown”);
var mdText=md.InvokeMember(“Parse”,BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static,null,
null,新对象[]{“**asdasd**”,false,false,false});
}
@mdText
这很好。因此程序集被加载,类型可以访问,但Razor似乎没有意识到这一点
因此,问题是:
是否可以在运行时加载程序集,并使它们在运行时编译时可供Razor使用,并像通常通过直接声明性访问使用类型一样使用它?快速查看ASP.NET核心源代码可以发现: 所有Razor视图编译都从以下位置开始: (……) 它使用: (……,参考文献:…) 它使用: 它使用:
//简化
var referencePaths=ApplicationPartManager.ApplicationParts
第()类
.SelectMany(=>u0.GetReferencePath())
它使用:
因此我们需要以某种方式注册我们自己的ICompilationReferencesProvider
,这就是..
应用程序部件管理器
在搜索应用程序部件时,ApplicationPartManager
会做一些事情:
[assembly:ApplicationPartAttribute(assemblyName:“…”)]//指定要添加为ApplicationPart的程序集
[assembly:RelatedAssemblyAttribute(assemblyFileName:“…”)]//指定要作为MVC程序集发现机制的一部分加载的程序集。
//plus`Assembly.GetEntryAssembly()`在后台自动添加。
ApplicationPartFactory
的类型ApplicationPartFactory
s调用方法GetApplicationParts(assembly)
ApplicationPartFactory的所有程序集
获取DefaultApplicationPartFactory
,该程序集在GetApplicationParts
中返回新的程序集(程序集)
公共抽象IEnumerable GetApplicationParts(汇编);
GetApplicationPartFactory
GetApplicationPartFactory搜索[assembly:ProvideApplicationPartFactory(typeof(SomeType))]
然后使用SomeType
作为工厂
公共抽象类ApplicationPartFactory{
公共抽象IEnumerable GetApplicationParts(组装);
公共静态应用程序PartFactory GetApplicationPartFactory(程序集)
{
// ...
var providedAttribute=assembly.GetCustomAttribute();
if(provideAttribute==null)
{
返回DefaultApplicationPartFactory.Instance;//此函数将'assembly'注册为'new AssemblyPart(assembly)`
}
var type=providedAttribute.GetFactoryType();
// ...
return(ApplicationPartFactory)Activator.CreateInstance(类型);
}
}
一个解决方案
这意味着我们可以创建并注册(使用ProvideApplicationPartFactoryAttribute
)我们自己的ApplicationPartFactory
,它返回一个自定义ApplicationPart
实现ICompilationReferenceProvider
的实现,然后在GetReferencePaths
中返回我们的引用
[程序集:ProvideApplicationPartFactory(typeof(MyApplicationPartFactory))]
命名空间WebApplication1{
公共类MyApplicationPartFactory:ApplicationPartFactory{
公共覆盖IEnumerable GetApplicationParts(Asse
@page
<pre>
output: [
@(
new ClassLibrary1.Class1().Method1()
)
]
</pre>
output: [
Hallo, World!
]