使用RazorEngine解析剃刀同时模板 [英] Using RazorEngine to parse Razor templates concurrently

查看:722
本文介绍了使用RazorEngine解析剃刀同时模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用RazorEngine库( HTTP://razorengine.$c$cplex.com/ )在一个MVC 3 Web应用程序使用剃刀模板语言来解析字符串(不在视图)。

在一般情况下,这工作正常。然而,当多个用户访问code,在同一时间解析剃须刀模板,我偶尔看到,看起来像是发生在内部剃刀编译器错误(见他们两个在下面)。我有麻烦间preting这些错误,但我的猜测是,我调用剃刀编译器的方式并不安全的并发

这是一个已知的问题与剃刀编译器?如何正常剃刀的意见( .cshtml )没有碰到这个问题?对此有一个解决办法比包装我的所有应用程序的调用Razor.Parse在互斥?

我调用code是如下,只是围绕一个简单的包装 Razor.Parse

 保护串ParseTemplate< T>(串templateString,T型)
    {
        //由NUnit的需要prevent模板编译错误,这binderAssembly线
        VAR binderAssembly = typeof运算(Microsoft.CSharp.RuntimeBinder.Binder).Assembly;
        VAR的结果= Razor.Parse(templateString,模型);
        返回结果;
    }

误区一:

  System.ArgumentOutOfRangeException:索引超出范围。必须是非负并小于集合的大小。
参数名:chunkLength
在System.Text.StringBuilder.ToString()
在System.Web.Razor.Generator.Razor codeGenerator.BlockContext.MarkEndGenerated code()
在System.Web.Razor.Generator.Razor codeGenerator.WriteBlock(BlockContext块)
在System.Web.Razor.Parser.ParserContext.FlushNextOutputSpan()
在System.Web.Razor.Parser.ParserContext.StartBlock(BlockType blockType,布尔outputCurrentBufferAsTransition)
在System.Web.Razor.Parser.ParserBase.ParseComment()
在System.Web.Razor.Parser.ParserBase.TryParseComment(SpanFactory previousSpanFactory)
在System.Web.Razor.Parser.ParserBase.ParseBlockWithOtherParser(SpanFactory previousSpanFactory,布尔collectTransitionToken)
在System.Web.Razor.Parser.HtmlMarkupParser.TryStart codeParser(布尔isSingleLineMarkup,布尔documentLevel)
在System.Web.Razor.Parser.HtmlMarkupParser.ParseRootBlock(Tuple`2 nestingSequences,布尔的CaseSensitive)
在System.Web.Razor.Parser.RazorParser.Parse(LookaheadTextReader输入,ParserVisitor访客)
在System.Web.Razor.RazorTemplateEngine.Generate codeCore(LookaheadTextReader输入字符串的className,字符串rootNamespace,字符串sourceFileName,Nullable`1 cancelToken)
在System.Web.Razor.RazorTemplateEngine.Generate code(TextReader的输入字符串的className,字符串rootNamespace,字符串sourceFileName,Nullable`1 cancelToken)
在System.Web.Razor.RazorTemplateEngine.Generate code(TextReader的输入)
在RazorEngine.Compilation.CompilerServiceBase.Get codeCompileUnit(字符串的className,字符串模板,ISet`1 namespaceImports,类型templateType,类型modelType)
在RazorEngine.Compilation.DirectCompilerServiceBase.Compile(TypeContext上下文)
在RazorEngine.Compilation.DirectCompilerServiceBase.CompileType(TypeContext上下文)
在RazorEngine.Templating.TemplateService.CreateTemplate(字符串模板,类型modelType)
在RazorEngine.Templating.TemplateService.Parse [T](字符串模板,T型,字符串名称)
在RazorEngine.Razor.Parse [T](字符串模板,T型,字符串名称)

误区之二:

  System.ObjectDisposedException:不能从一个封闭的TextReader读取。
在System.IO.StringReader.Read()
在System.Web.Razor.Text.BufferingTextReader.NextCharacter()
在System.Web.Razor.Text.BufferingTextReader.Read()
在System.Web.Razor.Parser.ParserContext.AcceptCurrent()
在System.Web.Razor.Parser.HtmlMarkupParser.ParseRootBlock(Tuple`2 nestingSequences,布尔的CaseSensitive)
在System.Web.Razor.Parser.RazorParser.Parse(LookaheadTextReader输入,ParserVisitor访客)
在System.Web.Razor.RazorTemplateEngine.Generate codeCore(LookaheadTextReader输入字符串的className,字符串rootNamespace,字符串sourceFileName,Nullable`1 cancelToken)
在System.Web.Razor.RazorTemplateEngine.Generate code(TextReader的输入字符串的className,字符串rootNamespace,字符串sourceFileName,Nullable`1 cancelToken)
在System.Web.Razor.RazorTemplateEngine.Generate code(TextReader的输入)
在RazorEngine.Compilation.CompilerServiceBase.Get codeCompileUnit(字符串的className,字符串模板,ISet`1 namespaceImports,类型templateType,类型modelType)
在RazorEngine.Compilation.DirectCompilerServiceBase.Compile(TypeContext上下文)
在RazorEngine.Compilation.DirectCompilerServiceBase.CompileType(TypeContext上下文)
在RazorEngine.Templating.TemplateService.CreateTemplate(字符串模板,类型modelType)
在RazorEngine.Templating.TemplateService.Parse [T](字符串模板,T型,字符串名称)
在RazorEngine.Razor.Parse [T](字符串模板,T型,字符串名称)


解决方案

更新:
据他们的团队博客文章,最新版本3.x(的on Github上)现在是线程安全的。我还没有核实其线程安全的真实性,但认为它已经被正确执行。请考虑这个答案只是由于历史的目的有用的其余部分。


从code来看,本项目不看远程线程安全的。

Razor.Parse

 公共静态字符串解析< T>(字符串模板,T型,字符串名称= NULL)
{
    返回DefaultTemplateService.Parse< T>(模板,型号,名称);
}

TemplateService.Parse

 公共字符串解析< T>(字符串模板,T型,字符串名称= NULL)
{
    VAR实例= GetTemplate(模板的typeof(T),名);
    ...
}

TemplateService.GetTemplate

 内部的ITemplate GetTemplate(字符串模板,类型modelType,字符串名称)
{
    如果(!string.IsNullOrEmpty(名))
        如果(templateCache.ContainsKey(名))
            返回templateCache [名]    VAR实例= CreateTemplate(模板,modelType);    如果(!string.IsNullOrEmpty(名))
        如果(!templateCache.ContainsKey(名))
            templateCache.Add(名称,实例);    返回实例;
}

因此​​, Razor.Parse 是一个静态方法。 DefaultTemplateService 剃须刀解析和静态属性 GetTemplate 是实例方法,但静态有效调用,因为静态 DefaultTemplateService 的。这意味着所有的线程都要经过同样的实例,并通过 GetTemplate 。你会发现, GetTemplate 不获取任何锁变异状态( templateCache )。因此,code不是线程安全的。

I'm using the RazorEngine library (http://razorengine.codeplex.com/) in an MVC 3 web application to parse strings (that aren't views) using the Razor templating language.

In general, this works fine. However, when multiple users are accessing code that parses Razor templates at the same time, I occasionally see errors that look like they occur in the internal Razor compiler (see two of them below). I'm having trouble interpreting these errors, but my guess is that the way I'm invoking the Razor compiler is not concurrency safe.

Is this a known issue with the Razor compiler? How do normal Razor views (.cshtml) not run into this problem? Is there a workaround for this better than wrapping all of my application's calls to Razor.Parse in a mutex?

My calling code is as follows, just a simple wrapper around 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;
    }

Error one:

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)

Error two:

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)

解决方案

Update: According to a blog post from their team, the latest version 3.x (on Github) is now thread-safe. I have not vetted the veracity of its thread-safety, but assume it has been implemented properly. Please consider the rest of this answer useful only for historical purposes.


Judging from the code, this project doesn't look remotely thread-safe.

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;
}

So, Razor.Parse is a static method. DefaultTemplateService is a static property on Razor, and Parse and GetTemplate are instance methods, but effectively invoked statically because of the static DefaultTemplateService. This means all threads go through the same instance and go through GetTemplate. You'll notice that GetTemplate mutates state (templateCache) without acquiring any locks. Therefore, this code is not threadsafe.

这篇关于使用RazorEngine解析剃刀同时模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆