Быстрый просмотр исходного кода ASP.NET Core показывает:
Все представления Razor Компиляции начинаются с:
RuntimeViewCompiler.CreateCompilation (..)
, который использует: CSharpCompiler.Create (.., .., ссылки: ..)
, который использует: RazorReferenceManager.CompilationReferences
, который использует: см. Код на 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 (сборка) (, как видно в строке 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);
}
}
One Solution
Это означает, что мы можем создать и зарегистрировать (используя ProvideApplicationPartFactoryAttribute
) нашу собственную ApplicationPartFactory
, которая возвращает пользовательскую реализацию ApplicationPart
, которая реализует ICompilationReferencesProvider
, а затем возвращает наши ссылкив GetReferencePaths
.
[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);
}
}
}
Настройка моего рабочего теста:
- Веб-приложение ASP.NET Core 3
- Класс ASP.NET Core 3 ClassLibrary
- В обоих проектах нет ссылок друг на друга.
<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>
services
.AddRazorPages()
.AddRazorRuntimeCompilation();
AssemblyLoadContext.Default.LoadFromAssemblyPath(@"C:\path\to\ClassLibrary1.dll");
// plus the MyApplicationPartFactory and attribute from above.
~ / Pages / Index.cshtml
<code>@page
<pre>
output: [
@(
new ClassLibrary1.Class1().Method1()
)
]
И он показывает ожидаемый результат:
output: [
Hallo, World!
]
Хорошего дня.