使用RazorEngine同时解析Razor模板

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

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

这是Razor编译器的已知问题吗? 正常的Razor视图( .cshtml )如何不遇到这个问题? 有没有一种解决方法比将我的所有应用程序的调用包含在互斥锁中的Razor.Parse更好?

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

  protected string ParseTemplate(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) 

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


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

Razor.Parse

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

TemplateService.Parse

 public string Parse(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 )。 因此,此代码不是线程安全的。