c# - 如何让 Razor 从模板服务中的另一个程序集中查找 View

标签 c# razor .net-core asp.net-core-2.1 asp.net-mvc-areas

我正在尝试从另一个项目加载 Razor View 。我已经进入了几个不同的兔子洞,但到目前为止我还没有找到一种方法来完成这项工作。但是,根据我的研究,似乎有两种主要方法可以使它起作用。

一种选择是使用嵌入式资源,然后将嵌入式文件提供程序连接到 razor。我可能是错的,但我认为这是 .net core 2.1 之前的方法。我的理解是,在 2.1 Razor 中, View 是在构建时编译的。将其设置为嵌入式将保存实际文件,这对旧的运行时编译很有用。

// Add the embedded file provider to be used with razor view templates
var viewAssembly = typeof(CoreStartup).GetTypeInfo().Assembly;
var fileProvider = new EmbeddedFileProvider(viewAssembly);
services.Configure<RazorViewEngineOptions>(o => o.FileProviders.Add(fileProvider));
services.AddTransient<ITemplateService, TemplateService>();

另一种方法是将 View 放在区域文件夹中。我什至找到了 sample project这表明你完全可以做到!但是,我一直无法找到获得相同结果的方法。

这里的引用是我试图用来查找和呈现 Razor View 的服务。我需要从中获取 html 输出以帮助创建 html 电子邮件模板。

namespace TestApp.Services
{
    using System;
    using System.IO;
    using System.Threading.Tasks;

    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Abstractions;
    using Microsoft.AspNetCore.Mvc.ModelBinding;
    using Microsoft.AspNetCore.Mvc.Razor;
    using Microsoft.AspNetCore.Mvc.Rendering;
    using Microsoft.AspNetCore.Mvc.ViewFeatures;
    using Microsoft.AspNetCore.Routing;
    using Microsoft.Extensions.Options;

    using Serilog;

    public class TemplateService : ITemplateService
    {
        private readonly IServiceProvider _serviceProvider;
        private readonly ITempDataProvider _tempDataProvider;
        private readonly IRazorViewEngine _viewEngine;

        public TemplateService(
            IServiceProvider serviceProvider,
            ITempDataProvider tempDataProvider,
            IRazorViewEngine viewEngine)
        {
            this._serviceProvider = serviceProvider;
            this._tempDataProvider = tempDataProvider;
            this._viewEngine = viewEngine;
        }

        public async Task<string> RenderTemplateAsync<TViewModel>(string viewPath, TViewModel viewModel)
        {
            var httpContext = new DefaultHttpContext
                                  {
                                      RequestServices = this._serviceProvider
                                  };

            return await RenderTemplateAsync(httpContext, viewPath, viewModel);
        }

        public async Task<string> RenderTemplateAsync<TViewModel>(HttpContext httpContext, string viewPath, TViewModel viewModel)
        {
            var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());

            var viewResult = this._viewEngine.FindView(actionContext, viewPath, false);
            if (!viewResult.Success)
            {
                Log.Error("Failed to render template {@viewPath} because it was not found.", viewPath);
                throw new FileNotFoundException($"Failed to render template {viewPath} because it was not found.");
            }

            var viewDictionary = new ViewDataDictionary<TViewModel>(new EmptyModelMetadataProvider(), new ModelStateDictionary());
            var tempDataDictionary = new TempDataDictionary(httpContext, this._tempDataProvider);

            using (var outputWriter = new StringWriter())
            {
                try
                {
                    var viewContext = new ViewContext(
                        actionContext, 
                        viewResult.View, 
                        viewDictionary,
                        tempDataDictionary, 
                        outputWriter, 
                        new HtmlHelperOptions());
                    viewContext.ViewData.Model = viewModel;

                    await viewResult.View.RenderAsync(viewContext);
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "Failed to render template due to a razor engine failure");
                    throw;
                }

                return outputWriter.ToString().Replace("\r\n", string.Empty);
            }
        }
    }
}

我是这样调用服务的。我已经尝试了我能想到的所有可能的字符串来尝试让它工作。问题是我不确定用于查找区域 View 的确切格式。所以我不确定是路由错了还是没接好。

var body = await _templateService.RenderTemplateAsync(HttpContext, "Common/EmailDetails", emailModel);

现在我正在尝试获取 EmailDetails.cshtml 共享 View 。

/Areas
    /Common
        /Views
            /Shared
                -EmailDetails.cshtml

最佳答案

请检查这个 这仅适用于在应用程序目录中搜索 View :

public class MyViewLocationExpander : IViewLocationExpander
{

    public void PopulateValues(ViewLocationExpanderContext context)
    {

    }

    public virtual IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
    {
        return viewLocations.Select(f => f.Replace("/Views/", "/Areas/Common/Views/")); 
    }

}

在启动类中:

services.Configure<RazorViewEngineOptions>(options =>
        {                
            options.ViewLocationExpanders.Add( new MyViewLocationExpander());                
        });

如果您需要从应用“外部”加载 View ,请检查:https://www.mikesdotnetting.com/article/301/loading-asp-net-core-mvc-views-from-a-database-or-other-location

关于c# - 如何让 Razor 从模板服务中的另一个程序集中查找 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52668228/

相关文章:

c# - AsyncLocal 值与 TPL 数据流不正确

c# - 在 C# 中反序列化 JSON 数组(或列表)

c# - @Html.ActionLink 不工作

c# - CSHTML 文件无法在 VS2013 中打开

c# - 如何在两个不同的 .NET Core 应用程序之间共享加密数据?

linux - 在 Linux .NET Core 上生成类似 Windows 的友好时区名称列表

c# - 键盘按键本地化

c# - 从C#应用程序禁用浏览器?

c# - 如何在 MVC 4 中添加 View

.net - 从.Net Core api返回html文件