如何从Razor Pages应用程序的插件动态加载页面?

时间:2018-12-28 07:02:18

标签: c# asp.net-core razor-pages

我正在尝试使用Razor Pages应用程序处理插件。

解决方案包含3个项目:一个Razor Pages应用程序和两个Razor类库(RCL)。应用程序一定不能静态引用RCL项目,它们必须作为插件加载:

enter image description here

页面内部没有特殊之处。功能页面仅生成简单的HTML。索引页面建立了一种菜单。

索引页模型:

public class IndexModel : PageModel
{
    public IEnumerable<MenuItem> MenuItems { get; private set; }

    public void OnGet()
    {
        MenuItems = new List<MenuItem>
        {
            new MenuItem { Route = "FeatureA", Title = "Feature A" },
            new MenuItem { Route = "FeatureB", Title = "Feature B" }
        };
    }
}

索引页:

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="text-center">
    <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
        <ul class="navbar-nav flex-grow-1">
            @foreach (var item in Model.MenuItems)
            {
                <li class="nav-item">
                    <a class="nav-link text-dark" asp-area="" asp-page="/@item.Route">@item.Title</a>
                </li>
            }
        </ul>
    </div>
</div>

当我运行该应用程序时,有一些菜单项,但是它们的href是空的:

<div class="text-center">
    <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
        <ul class="navbar-nav flex-grow-1">
                <li class="nav-item">
                    <a class="nav-link text-dark" href="">Feature A</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link text-dark" href="">Feature B</a>
                </li>
        </ul>
    </div>
</div>

当然,所有程序集(应用程序和功能部件)都在同一目录中。

菜单在以下两种情况下有效:

  • 如果我在App项目中引用RCL项目,这会杀死插件的想法;
  • 或者如果我将App.deps.jsonFeatureLib_AFeatureLib_B作为从属关系(只需从第一种情况保存deps文件,删除引用,全部重建,复制保存的deps文件)。

此外,我试图在Startup类中急切地加载RCL程序集。 程序集正在加载,但是Index页面的行为相同。

有没有办法告诉ASP基础结构使用RCL程序集而不修改deps文件?我想念什么?

2 个答案:

答案 0 :(得分:2)

我知道了。

基本思想是为ApplicationPartManager提供适当的应用程序部分。
重要的是要注意:

  • “代码”程序集(例如FeatureLib_A.dll)必须作为AssemblyPart添加;
  • “视图”程序集(例如FeatureLib_A.Views.dll)必须添加为CompiledRazorAssemblyPart

示例代码:

public class Startup
{
    // ...

    public void ConfigureServices(IServiceCollection services)
    {
        var assemblyLoader = new DotNetCoreAssemblyLoader(searchPattern: "FeatureLib*.dll");

        services.AddMvc()
            .ConfigureApplicationPartManager(_ =>
            {
                foreach (var assembly in assemblyLoader.Assemblies)
                {
                    if (assembly.FullName.Contains("Views"))
                    {
                        _.ApplicationParts.Add(new CompiledRazorAssemblyPart(assembly));
                    }
                    else
                    {
                        _.ApplicationParts.Add(new AssemblyPart(assembly));
                    }
                }
            })
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

    // ...
}

DotNetCoreAssemblyLoader是一个自定义类,它使用给定的搜索模式查找程序集文件,并通过AssemblyLoadContext.Default.LoadFromAssemblyPath加载程序集。

答案 1 :(得分:0)

public class Startup
{

    public Startup( IHostingEnvironment hostingEnvironment)
    {

        _hostingEnvironment = hostingEnvironment;
    }
    private readonly IHostingEnvironment _hostingEnvironment;



    public void ConfigureServices(IServiceCollection services)
    {
        ...
        services.AddMvc()
                      .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
                      .ConfigureApplicationPartManager(ConfigureApplicationParts); ;
    }



    private void ConfigureApplicationParts(ApplicationPartManager apm)
    {
        string rootPath = _hostingEnvironment.ContentRootPath;
        var pluginsPath = Path.Combine(rootPath, "Plugins");

        var assemblyFiles = Directory.GetFiles(pluginsPath, "Plugin*.dll", SearchOption.AllDirectories);
        foreach (var assemblyFile in assemblyFiles)
        {
            try
            {
                var assembly = Assembly.LoadFrom(assemblyFile);
                if (assemblyFile.EndsWith(".Views.dll"))
                    apm.ApplicationParts.Add(new 
                           CompiledRazorAssemblyPart(assembly));
                else
                    apm.ApplicationParts.Add(new AssemblyPart(assembly));
            }
            catch (Exception e) { }
        }
    }
}