c# - 使用 RazorEngine 并发解析 Razor 模板

标签 c# asp.net-mvc concurrency razor

我在 MVC 3 网络应用程序中使用 RazorEngine 库 ( http://razorengine.codeplex.com/ ) 来使用 Razor 模板语言解析字符串(不是 View )。

一般来说,这工作正常。但是,当多个用户同时访问解析 Razor 模板的代码时,我偶尔会看到看起来像是在内部 Razor 编译器中发生的错误(请参阅下面的两个错误)。我在解释这些错误时遇到了问题,但我的猜测是我调用 Razor 编译器的方式不是并发安全的。

这是 Razor 编译器的已知问题吗?普通 Razor View (.cshtml) 怎么不会遇到这个问题?有没有比将我的应用程序对 Razor.Parse 的所有调用包装在互斥锁中更好的解决方法?

我的调用代码如下,只是对 Razor.Parse 的简单包装:

    protected string ParseTemplate<T>(string templateString, T model)
    {
        //This binderAssembly line is required by NUnit to prevent template compilation errors
        var binderAssembly = typeof(Microsoft.CSharp.RuntimeBinder.Binder).Assembly;
        var result = Razor.Parse(templateString, model);
        return result;
    }

错误一:

System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: chunkLength
at System.Text.StringBuilder.ToString()
at System.Web.Razor.Generator.RazorCodeGenerator.BlockContext.MarkEndGeneratedCode()
at System.Web.Razor.Generator.RazorCodeGenerator.WriteBlock(BlockContext block)
at System.Web.Razor.Parser.ParserContext.FlushNextOutputSpan()
at System.Web.Razor.Parser.ParserContext.StartBlock(BlockType blockType, Boolean outputCurrentBufferAsTransition)
at System.Web.Razor.Parser.ParserBase.ParseComment()
at System.Web.Razor.Parser.ParserBase.TryParseComment(SpanFactory previousSpanFactory)
at System.Web.Razor.Parser.ParserBase.ParseBlockWithOtherParser(SpanFactory previousSpanFactory, Boolean collectTransitionToken)
at System.Web.Razor.Parser.HtmlMarkupParser.TryStartCodeParser(Boolean isSingleLineMarkup, Boolean documentLevel)
at System.Web.Razor.Parser.HtmlMarkupParser.ParseRootBlock(Tuple`2 nestingSequences, Boolean caseSensitive)
at System.Web.Razor.Parser.RazorParser.Parse(LookaheadTextReader input, ParserVisitor visitor)
at System.Web.Razor.RazorTemplateEngine.GenerateCodeCore(LookaheadTextReader input, String className, String rootNamespace, String sourceFileName, Nullable`1 cancelToken)
at System.Web.Razor.RazorTemplateEngine.GenerateCode(TextReader input, String className, String rootNamespace, String sourceFileName, Nullable`1 cancelToken)
at System.Web.Razor.RazorTemplateEngine.GenerateCode(TextReader input)
at RazorEngine.Compilation.CompilerServiceBase.GetCodeCompileUnit(String className, String template, ISet`1 namespaceImports, Type templateType, Type modelType)
at RazorEngine.Compilation.DirectCompilerServiceBase.Compile(TypeContext context)
at RazorEngine.Compilation.DirectCompilerServiceBase.CompileType(TypeContext context)
at RazorEngine.Templating.TemplateService.CreateTemplate(String template, Type modelType)
at RazorEngine.Templating.TemplateService.Parse[T](String template, T model, String name)
at RazorEngine.Razor.Parse[T](String template, T model, String name)

错误二:

System.ObjectDisposedException: Cannot read from a closed TextReader.
at System.IO.StringReader.Read()
at System.Web.Razor.Text.BufferingTextReader.NextCharacter()
at System.Web.Razor.Text.BufferingTextReader.Read()
at System.Web.Razor.Parser.ParserContext.AcceptCurrent()
at System.Web.Razor.Parser.HtmlMarkupParser.ParseRootBlock(Tuple`2 nestingSequences, Boolean caseSensitive)
at System.Web.Razor.Parser.RazorParser.Parse(LookaheadTextReader input, ParserVisitor visitor)
at System.Web.Razor.RazorTemplateEngine.GenerateCodeCore(LookaheadTextReader input, String className, String rootNamespace, String sourceFileName, Nullable`1 cancelToken)
at System.Web.Razor.RazorTemplateEngine.GenerateCode(TextReader input, String className, String rootNamespace, String sourceFileName, Nullable`1 cancelToken)
at System.Web.Razor.RazorTemplateEngine.GenerateCode(TextReader input)
at RazorEngine.Compilation.CompilerServiceBase.GetCodeCompileUnit(String className, String template, ISet`1 namespaceImports, Type templateType, Type modelType)
at RazorEngine.Compilation.DirectCompilerServiceBase.Compile(TypeContext context)
at RazorEngine.Compilation.DirectCompilerServiceBase.CompileType(TypeContext context)
at RazorEngine.Templating.TemplateService.CreateTemplate(String template, Type modelType)
at RazorEngine.Templating.TemplateService.Parse[T](String template, T model, String name)
at RazorEngine.Razor.Parse[T](String template, T model, String name)

最佳答案

更新: 根据blog post他们团队的最新版本 3.x ( on Github ) 现在是线程安全的。我没有审查它的线程安全性的准确性,但假设它已被正确实现。请考虑此答案的其余部分仅对历史目的有用。


从代码来看,这个项目看起来并不是线程安全的。

Razor.Parse:

public static string Parse<T>(string template, T model, string name = null)
{
    return DefaultTemplateService.Parse<T>(template, model, name);
}

TemplateService.Parse:

public string Parse<T>(string template, T model, string name = null)
{
    var instance = GetTemplate(template, typeof(T), name);
    ...
}

TemplateService.GetTemplate:

internal ITemplate GetTemplate(string template, Type modelType, string name)
{
    if (!string.IsNullOrEmpty(name))
        if (templateCache.ContainsKey(name))
            return templateCache[name];

    var instance = CreateTemplate(template, modelType);

    if (!string.IsNullOrEmpty(name))
        if (!templateCache.ContainsKey(name))
            templateCache.Add(name, instance);

    return instance;
}

所以,Razor.Parse 是一个静态方法。 DefaultTemplateServiceRazor 上的静态属性,ParseGetTemplate 是实例方法,但有效地静态调用是因为静态 DefaultTemplateService。这意味着所有线程都经过同一个实例并经过 GetTemplate。您会注意到 GetTemplate 改变了状态 (templateCache) 而没有获取任何锁。因此,这段代码不是线程安全的。

关于c# - 使用 RazorEngine 并发解析 Razor 模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6444277/

相关文章:

javascript - 将 View 模型映射到 KnockoutJS 验证

c# - 下拉 future 10 年

c# - 在 Ubuntu 下使用 mono 运行使用 VS2015 开发的 ASP.Net MVC 项目

Golang grpc : how to determine when the server has started listening?

go - 如何使用 channel 收集各种goroutine的响应

c# - 如何获得实例化泛型的参数类型?

c# - app.config 中的嵌套自定义元素 (C#)

Swift 队列/并发和锁定

c# - HttpRequest 与 HttpWebRequest

c# - 使用 Entity Framework 4 执行自定义 sql