使用RazorEngine同时解析Razor模板
本文关键字:Razor 模板 RazorEngine 使用 | 更新日期: 2023-09-27 17:59:54
我正在使用RazorEngine库(http://razorengine.codeplex.com/)在MVC 3 web应用程序中,使用Razor模板语言解析字符串(不是视图)。
总的来说,这很好用。然而,当多个用户同时访问解析Razor模板的代码时,我偶尔会看到内部Razor编译器中出现的错误(见下面的两个)。我在解释这些错误时遇到了困难,但我的猜测是,我调用Razor编译器的方式是不安全的。
这是Razor编译器的已知问题吗?普通Razor视图(.cshtml
)如何不遇到此问题?有没有一种变通方法比包装我的应用程序对Razor的所有调用更好。在互斥对象中进行分析?
我的调用代码如下,只是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)
更新:根据他们团队的一篇博客文章,最新版本3.x(在Github上)现在是线程安全的。我没有审查其线程安全性的真实性,但认为它已经得到了正确的实施。请考虑这个答案的其余部分仅用于历史目的。
从代码来看,这个项目看起来并不是线程安全的。
剃刀。分析:
public static string Parse<T>(string template, T model, string name = null)
{
return DefaultTemplateService.Parse<T>(template, model, name);
}
模板服务。分析:
public string Parse<T>(string template, T model, string name = null)
{
var instance = GetTemplate(template, typeof(T), name);
...
}
模板服务。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
是一种静态方法。DefaultTemplateService
是Razor
上的一个静态属性,Parse
和GetTemplate
是实例方法,但由于静态DefaultTemplateService
而被有效地静态调用。这意味着所有线程都经过同一个实例并经过GetTemplate
。您会注意到GetTemplate
在不获取任何锁的情况下突变状态(templateCache
)。因此,此代码不是线程安全的。