这个练习的内容
在本文中,我们将研究这两个用例:
主应用程序静态引用的RCL
由主应用程序动态加载的RCL。
两个rcl都包含静态文件,即javasript和css文件。
我们将使用Asp.Net核心MVC Web应用程序和两个rcl。
为开始创建Asp.Net核心MVC Web应用程序,并将其命名为WebApp。
参考RCL
按照文档提供的说明创建RCL。
将RCL命名为StaticRCL。我们稍后再看为什么名字很重要。
从项目中删除所有文件和文件夹,并添加三个新文件夹、Controller、Views和wwwroot。
在Controllers文件夹中创建控制器类。
public class LibController : Controller
{
[Route("/static")]
public IActionResult Index()
{
return View();
}
}
在Views文件夹中创建Lib文件夹。添加Index.cshtml查看文件。
<script src="~/_content/StaticRCL/js/script.js"></script>
<div>
<strong>STATICALLY</strong> referenced Razor Class Library
</div>
<div>
<button onclick="StaticRCL_ShowMessage();">Click Me!</button>
</div>
在wwwroot文件夹中创建一个js文件夹。添加script.js文件。
function StaticRCL_ShowMessage() {
alert('Hi from Statically refernced Razor Class Library javascript');
}
一种动态可加载的RCL
Controller
public class LibDynamicController : Controller
{
[Route("/dynamic")]
public IActionResult Index()
{
return View();
}
}
View.
<script src="js/script.js"></script>
<div>
<strong>DYNAMICALLY</strong> loaded Razor Class Library
</div>
<div>
<button onclick="DynamicRCL_ShowMessage();">Click Me!</button>
</div>
Javascript 文件
function DynamicRCL_ShowMessage() {
alert('Hi from Dynamically loaded Razor Class Library javascript');
}
我们还需要做以下工作。<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<AssemblyName>rcl_DynamicRCL</AssemblyName>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>..\WebApp\bin\Debug\</OutputPath>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.0" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="wwwroot\**\*" />
</ItemGroup>
</Project>
处理参考RCL@{
ViewData["Title"] = "Home Page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<div><a href="/static">Statically referenced Razor Class Library View</a></div>
<div><a href="/dynamic">Dynamically loaded Razor Class Library View</a></div>
</div>
如您所见,有两个锚元素调用相应的RCL路由。public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews().
ConfigureApplicationPartManager((PartManager) => {
ConfigureStaticLibraries(PartManager); // static RCLs
LoadDynamicLibraries(PartManager); // dynamic RCLs
});
}
ApplicationPartManager类管理Asp.Net核心MVC或Razor页面应用程序。void ConfigureStaticLibraries(ApplicationPartManager PartManager)
{
Assembly Assembly = typeof(StaticRCL.Controllers.LibController).Assembly;
ApplicationPart ApplicationPart = new AssemblyPart(Assembly);
PartManager.ApplicationParts.Add(ApplicationPart);
}
上述方法在路由到Razor视图(和Razor页面)时效果良好。当涉及到静态文件(比如javascript和css文件)时,会有一些变化。<script src="~/_content/StaticRCL/js/script.js"></script>
处理动态加载的RCL public class LibraryLoadContext: AssemblyLoadContext
{
private AssemblyDependencyResolver fResolver;
public LibraryLoadContext(string BinFolder)
{
fResolver = new AssemblyDependencyResolver(BinFolder);
}
protected override Assembly Load(AssemblyName assemblyName)
{
string assemblyPath = fResolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
{
return LoadFromAssemblyPath(assemblyPath);
}
return null;
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
string FilePath = fResolver.ResolveUnmanagedDllToPath(unmanagedDllName);
if (FilePath != null)
{
return LoadUnmanagedDllFromPath(FilePath);
}
return IntPtr.Zero;
}
}
我们在加载插件库时使用LibraryLoadContext。 void LoadDynamicLibraries(ApplicationPartManager PartManager)
{
// get the output folder of this application
string BinFolder = this.GetType().Assembly.ManifestModule.FullyQualifiedName;
BinFolder = Path.GetDirectoryName(BinFolder);
// get the full filepath of any dll starting with the rcl_ prefix
string Prefix = "rcl_";
string SearchPattern = $"{Prefix}*.dll";
string[] LibraryPaths = Directory.GetFiles(BinFolder, SearchPattern);
if (LibraryPaths != null && LibraryPaths.Length > 0)
{
// create the load context
LibraryLoadContext LoadContext = new LibraryLoadContext(BinFolder);
Assembly Assembly;
ApplicationPart ApplicationPart;
foreach (string LibraryPath in LibraryPaths)
{
// load each assembly using its filepath
Assembly = LoadContext.LoadFromAssemblyPath(LibraryPath);
// create an application part for that assembly
ApplicationPart = LibraryPath.EndsWith(".Views.dll") ? new CompiledRazorAssemblyPart(Assembly) as ApplicationPart : new AssemblyPart(Assembly);
// register the application part
PartManager.ApplicationParts.Add(ApplicationPart);
// if it is NOT the *.Views.dll add it to a list for later use
if (!LibraryPath.EndsWith(".Views.dll"))
DynamicallyLoadedLibraries.Add(Assembly);
}
}
}
现在是棘手的部分。 void RegisterDynamicLibariesStaticFiles(IWebHostEnvironment env)
{
IFileProvider FileProvider;
foreach (Assembly A in DynamicallyLoadedLibraries)
{
// create a "web root" file provider for the embedded static files found on wwwroot folder
FileProvider = new ManifestEmbeddedFileProvider(A, "wwwroot");
// register a new composite provider containing
// the old web root file provider
// and the new one we just created
env.WebRootFileProvider = new CompositeFileProvider(env.WebRootFileProvider, FileProvider);
}
}
上面的方法由Startup类的Configure()方法在app.UseStaticFiles文件()调用之前调用。 app.UseHttpsRedirection();
// register file providers for the dynamically loaded libraries
if (DynamicallyLoadedLibraries.Count > 0)
RegisterDynamicLibariesStaticFiles(env);
app.UseStaticFiles();
这是DynamicRCL项目中的Index.cshtml做的,为了使用javascript文件
<script src="js/script.js"></script>
这里没有使用_content/{LIBRARY NAME}/方案。我们只使用js文件夹,因为我们已经将DynamicRCL程序集的wwwroot文件夹注册为web根文件夹。
测试在:
Windows 10
Asp.Net Core 3.1
Microsoft Visual Studio 2019预览版,版本16.9.0预览版5.0