RazorEngine 3.4 抛出 System.ArgumentException: 带有缓存的 @Layout 和不同的 Models。

4
我在使用RazorEngine 3.4缓存时遇到了问题。我有几个电子邮件模板,它们都有相同的@Layout,但每个模板的Model不同。一切正常,直到我尝试使用缓存,我读到了不要使用缓存:"将导致可怕的性能和内存泄漏" 来自这里

所以我打开了它。这很简单,但引发了一个问题:当我尝试使用不同的Model解析另一个模板时,_Layout.cshtml也会与第一个Model类型一起缓存,这将导致异常抛出:"System.ArgumentException: Object of type '....model1...' cannot be converted to type '...model2...'."

我在"IsolatedTemplateServiceTestFixture.cs"中编写了两个单元测试来展示这个问题。第一个测试通过了,但第二个测试失败了,因为TemplateService.SetModelExplicit()函数想要为Layout设置不同的Model类型的属性。

private Mock<ITemplateResolver> _templateResolver;

    [Test]
    public void IsolatedTemplateService_CanParseTemplateWithLayout_WithOneSerializableModels_UseCache()
    {
        _templateResolver = new Mock<ITemplateResolver>();
        var config = new TemplateServiceConfiguration()
        {
            Resolver = _templateResolver.Object
        };

        using (var service = new TemplateService(config))
        {
            _templateResolver.Setup(i => i.Resolve("test")).Returns("<html>@RenderBody()</html>");

            const string template = @"@{Layout=""test"";}<h1>Hello @Model.Item1</h1>";
            const string expected = "<html><h1>Hello World</h1></html>";

            var model = new Tuple<string>("World");
            string result = service.Parse(template, model, null, "C1");
            string result2 = service.Parse(template, model, null, "C1");

            Assert.That(result == expected, "Result does not match expected: " + result);
            Assert.That(result2 == expected, "Result does not match expected: " + result2);
        }
    }

    [Test]
    public void IsolatedTemplateService_CanParseTemplateWithLayout_WithDifferentSerializableModels_UseCache()
    {
        _templateResolver = new Mock<ITemplateResolver>();
        var config = new TemplateServiceConfiguration()
        {
            Resolver = _templateResolver.Object
        };

        using (var service = new TemplateService(config))
        {
            _templateResolver.Setup(i => i.Resolve("test")).Returns("<html>@RenderBody()</html>");

            const string template = @"@{Layout=""test"";}<h1>Hello @Model.Item1</h1>";
            const string expected = "<html><h1>Hello World</h1></html>";

            var model = new Tuple<string>("World");
            string result = service.Parse(template, model, null, "C1");
            string result2 = service.Parse(template, model, null, "C1");

            const string template2 = @"@{Layout=""test"";}<h1>Hello2 @Model.Item1</h1>";
            const string expected2 = "<html><h1>Hello2 123</h1></html>";
            var model2 = new Tuple<int>(123);

            string result3 = service.Parse(template2, model2, null, "C2");

            Assert.That(result == expected, "Result does not match expected: " + result);
            Assert.That(result2 == expected, "Result does not match expected: " + result2);

            Assert.That(result3 == expected2, "Result does not match expected: " + result3);
        }
    }

我的问题是:有人遇到同样的问题吗?在它被修复之前(如果真的会修复),有什么“好”的解决方法?
更新:最新版本(目前为v.3.10)两个测试都通过了。所以这个问题已经被解决了。

1
寻求调试帮助的问题(“为什么这段代码不起作用?”)必须在问题本身中包含所需的行为、具体问题或错误以及重现它所需的最短代码。链接很好,但仅仅希望每个人都回答问题并链接到另一个网站意味着当该链接消失时,这个问题将变得无用。 - Erik Philips
可能不值得尝试去对抗这个问题。只需为每个模型创建一堆视图即可。 - Keith Payne
但是在15个模板中使用一个具有大量属性的“大”模型并不好。另一个开发人员不知道为特定模板设置哪个属性。我报告了这个问题,但还没有答复。也许我会创建一个拉取请求并自己修复它... - Major
请注意,最新的RazorEngine版本中,“just works”(只需工作)功能已经实现。 - matthid
1个回答

5
在RazorEngine中,即使您没有在布局中声明模型,布局也具有固定的类型。第一次通过编译模板来编译布局时,模板的模型类型也变成了布局的类型。正如您注意到的那样,当您尝试使用不同类型编译另一个模板时,这将会发生冲突。
您可以通过声明布局的模型类型为动态类型来解决这个问题,即@model dynamic
这应该能解决问题。实际的模板不需要进行修改。

嗨,谢谢,它起作用了。但是我读到它在高负载情况下存在一些问题https://github.com/Antaris/RazorEngine/issues/180 。也许ViewBag是更好的解决方案... - Major
ViewBag 对我来说似乎是一个相当糟糕的解决方案,因为在视图中没有强类型模型的好处。我确实看到了你链接的那个问题,并忽略了它,但现在你再提一下,我们的项目可能有同样的问题。我们的应用程序每天只发送几百封电子邮件,但在几周内就会耗尽内存。我怀疑是一些 COM 调用,但无法通过负载测试确定。也许我在错误的地方寻找。我应该运行不同的负载测试。 - bstenzel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接