FluentEmail.Razor 值不能为空。参数名称:operatingAssembly

FluentEmail.Razor Value cannot be null. Parameter name: operatingAssembly

我有一个 ASP.Net MVC 应用程序(针对 .Net Framework 4.7.2)。

我安装了 FluentEmail.Core、FluentEmail.Smtp 和 FluentEmail.Razor(所有版本都是 3.0.0)

FluentEmail.Razor 需要 LightRazor 版本 2.0.0-rc3,我不得不手动安装它,因为 nuget 默认设置只会下载最终版本(在发布此问题时,RazorLight 的最终稳定版本是版本 1.0.0 不符合 FluentEmail.Razor v 3.0.0).

的要求

在我的代码中,我发送的电子邮件没有这样的问题:

public async Task<ActionResult> Index()
{

       var sender = new SmtpSender(() => new SmtpClient("localhost"));
       Email.DefaultSender = sender;


      var email = await Email
         .From("from-test@gmail.com")
         .To("to-test@gmail.com")
         .Subject("subject-test")
         .Body("body test")
         .SendAsync();           // this works perfectly

    return View();
}

现在,当我尝试使用 Razor 时,出现错误:

Value cannot be null. Parameter name: operatingAssembly Here is what I changed:

public async Task<ActionResult> Index()
{

    var sender = new SmtpSender(() => new SmtpClient("localhost"));
    Email.DefaultSender = sender;
    Email.DefaultRenderer = new RazorRenderer();    // this line causes the error
    
    var email = await Email
         .From("from-test@gmail.com")
         .To("to-test@gmail.com")
         .Subject("subject-test")
        .UsingTemplate("Dear @Model.FirstName this is a test", new { FirstName = "Mike" })
        .SendAsync();           
    
    return View();
}

我搜索了这个问题的答案,发现我需要告诉 RazorEngine soem 关于模型的信息 typeof(dynamic) 但我不能那样做,我必须指定一个 class 名称.所以我为我的模型创建了一个专用的 class,但我无法将 RazorEngine 的引用传递给 FluentEmail 默认渲染器。

我做错了什么?

我能够使用简单的 ASP.Net MVC 应用程序重现该问题。

代码失败是因为 Assembly.GetEntryAssembly() returns null here 当它试图构建 RazorLightEngine 时。官方文档说 Assembly.GetEntryAssembly() :

Can return null when called from unmanaged code.

在您的情况下,出现此问题是因为您的代码可能 运行 来自非托管进程 (IIS Express)。

有人建议 this workaround,其中包括将 Assembly.GetEntryAssembly() 替换为 Assembly.GetCallingAssembly()(总是 returns 非空值):

new RazorLightEngineBuilder()
    .SetOperatingAssembly(operatingAssembly ?? Assembly.GetCallingAssembly())
    .UseFileSystemProject(Directory.GetCurrentDirectory())
    .UseMemoryCachingProvider()
    .Build();

但在您的情况下,您不能直接使用它,因为 RazorRenderer 不会公开 RazorLightEngine 的构建方式。

所以我认为您需要创建自己的自定义 RazorRenderer 来解决这个问题。

这是一个可用的渲染器示例(我从 here 复制了 RazorRenderer 代码,修改了第一个构造函数并删除了其他构造函数):

using FluentEmail.Core.Interfaces;
using FluentEmail.Razor;
using RazorLight;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;

namespace WebApplication2.Controllers
{
    public class CustomRazorRenderer : ITemplateRenderer
    {
        private readonly RazorLightEngine _engine;

        public CustomRazorRenderer(string root = null)
        {
            _engine = new RazorLightEngineBuilder()
                .SetOperatingAssembly(Assembly.GetCallingAssembly())
                .UseFileSystemProject(root ?? Directory.GetCurrentDirectory())
                .UseMemoryCachingProvider()
                .Build();
        }

        public Task<string> ParseAsync<T>(string template, T model, bool isHtml = true)
        {
            dynamic viewBag = (model as IViewBagModel)?.ViewBag;
            return _engine.CompileRenderStringAsync<T>(RazorRenderer.GetHashString(template), template, model, viewBag);
        }

        string ITemplateRenderer.Parse<T>(string template, T model, bool isHtml)
        {
            return ParseAsync(template, model, isHtml).GetAwaiter().GetResult();
        }

    }
}

并且可以这样使用:

public async Task<ActionResult> Index()
{

    var sender = new SmtpSender(() => new SmtpClient("localhost"));
    Email.DefaultSender = sender;
    Email.DefaultRenderer = new CustomRazorRenderer();

    var email = await Email
        .From("from-test@gmail.com")
        .To("to-test@gmail.com")
        .Subject("subject-test")
        .UsingTemplate("Dear @Model.FirstName this is a test", new { FirstName = "Mike" })
    .SendAsync();

    return View();
}