对ASP.NET Core源代码的快速浏览显示:
所有Razor视图的编译起始于:
RuntimeViewCompiler.CreateCompilation(..)
使用:
CSharpCompiler.Create(..,..,引用:..)
使用:
RazorReferenceManager.CompilationReferences
使用:see code on github
// simplyfied
var referencePaths = ApplicationPartManager.ApplicationParts
.OfType<ICompilationReferencesProvider>()
.SelectMany(_ => _.GetReferencePaths())
使用:
ApplicationPartManager.ApplicationParts
所以我们需要以某种方式注册我们自己的ICompilationReferencesProvider
,这就是方法。
ApplicationPartManager
在搜索应用程序部分时,ApplicationPartManager
会做一些事情:
- 它搜索隐藏的装配体,其属性如下:
[assembly: ApplicationPartAttribute(assemblyName:"..")] // Specifies an assembly to be added as an ApplicationPart
[assembly: RelatedAssemblyAttribute(assemblyFileName:"..")] // Specifies a assembly to load as part of MVC's assembly discovery mechanism.
// plus `Assembly.GetEntryAssembly()` gets added automaticly behind the scenes.
-
然后,它循环遍历所有找到的程序集,并使用ApplicationPartFactory.GetApplicationPartFactory(assembly)(as seen in line 69)来查找扩展ApplicationPartFactory
的类型。
-
然后在所有找到的GetApplicationParts(assembly)
上调用方法ApplicationPartFactory
。
所有不带有ApplicationPartFactory
的程序集都将得到DefaultApplicationPartFactory
,它会在new AssemblyPart(assembly)
中返回GetApplicationParts
。
public abstract IEnumerable<ApplicationPart> GetApplicationParts(Assembly assembly);
GetApplicationPartFactory
GetApplicationPartFactory搜索[assembly: ProvideApplicationPartFactory(typeof(SomeType))]
,然后将SomeType
用作工厂。
public abstract class ApplicationPartFactory {
public abstract IEnumerable<ApplicationPart> GetApplicationParts(Assembly assembly);
public static ApplicationPartFactory GetApplicationPartFactory(Assembly assembly)
{
// ...
var provideAttribute = assembly.GetCustomAttribute<ProvideApplicationPartFactoryAttribute>();
if (provideAttribute == null)
{
return DefaultApplicationPartFactory.Instance; // this registers `assembly` as `new AssemblyPart(assembly)`
}
var type = provideAttribute.GetFactoryType();
// ...
return (ApplicationPartFactory)Activator.CreateInstance(type);
}
}
一个解决方案
这意味着我们可以创建和注册(使用ProvideApplicationPartFactoryAttribute
)自己的ApplicationPartFactory
,它返回实现ApplicationPart
的自定义ICompilationReferencesProvider
实现,然后在{{ 1}}。
GetReferencePaths
我的工作测试设置:
- ASP.NET Core 3 WebApplication
- ASP.NET Core 3 ClassLibrary
- 两个项目彼此无关联。
[assembly: ProvideApplicationPartFactory(typeof(MyApplicationPartFactory))]
namespace WebApplication1 {
public class MyApplicationPartFactory : ApplicationPartFactory {
public override IEnumerable<ApplicationPart> GetApplicationParts(Assembly assembly)
{
yield return new CompilationReferencesProviderAssemblyPart(assembly);
}
}
public class CompilationReferencesProviderAssemblyPart : AssemblyPart,ICompilationReferencesProvider {
private readonly Assembly _assembly;
public CompilationReferencesProviderAssemblyPart(Assembly assembly) : base(assembly)
{
_assembly = assembly;
}
public IEnumerable<string> GetReferencePaths()
{
// your `LoadPrivateBinAssemblies()` method needs to be called before the next line executes!
// So you should load all private bin's before the first RazorPage gets requested.
return AssemblyLoadContext.GetLoadContext(_assembly).Assemblies
.Where(_ => !_.IsDynamic)
.Select(_ => new Uri(_.CodeBase).LocalPath);
}
}
}
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Content Remove="Pages\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.0.0" />
</ItemGroup>
</Project>
〜/ Pages / Index.cshtml
services
.AddRazorPages()
.AddRazorRuntimeCompilation();
AssemblyLoadContext.Default.LoadFromAssemblyPath(@"C:\path\to\ClassLibrary1.dll");
// plus the MyApplicationPartFactory and attribute from above.
它显示了预期的输出:
@page
<pre>
output: [
@(
new ClassLibrary1.Class1().Method1()
)
]
</pre>
今天愉快。
,
事实证明,解决方案是通过Razor运行时编译选项,该选项允许添加额外的“ ReferencePaths”,然后显式加载程序集。
在ConfigureServices()中:
services.AddRazorPages(opt => { opt.RootDirectory = "/"; })
.AddRazorRuntimeCompilation(
opt =>
{
opt.FileProviders.Add(new PhysicalFileProvider(WebRoot));
LoadPrivateBinAssemblies(opt);
});
然后:
private void LoadPrivateBinAssemblies(MvcRazorRuntimeCompilationOptions opt)
{
var binPath = Path.Combine(WebRoot,"PrivateBin");
if (Directory.Exists(binPath))
{
var files = Directory.GetFiles(binPath);
foreach (var file in files)
{
if (!file.EndsWith(".dll",StringComparison.CurrentCultureIgnoreCase) &&
!file.EndsWith(".exe",StringComparison.InvariantCultureIgnoreCase))
continue;
try
{
var asm = AssemblyLoadContext.Default.LoadFromAssemblyPath(file);
opt.AdditionalReferencePaths.Add(file);
}
catch (Exception ex)
{
...
}
}
}
}
关键是:
opt.AdditionalReferencePaths.Add(file);
这使得程序集对于Razor而言可见,但实际上并未加载。要加载它,您必须显式加载它:
AssemblyLoadContext.Default.LoadFromAssemblyPath(file);
将从路径加载程序集。请注意,此程序集必须具有的任何依赖项都必须在应用程序的启动路径中或从中加载的同一文件夹中可用。
注意:依赖项的加载顺序在这里可能很重要,或者未找到以前未添加的程序集作为依赖项(未试用)。
本文链接:https://www.f2er.com/3169593.html