Использование RazorEngine для одновременного анализа шаблонов Razor - PullRequest
15 голосов
/ 22 июня 2011

Я использую библиотеку RazorEngine (http://razorengine.codeplex.com/) в веб-приложении MVC 3 для анализа строк (которые не являются представлениями) с использованием языка шаблонов Razor.

В общем, это работаетОднако, когда несколько пользователей получают доступ к коду, который анализирует шаблоны Razor одновременно, я иногда вижу ошибки, которые выглядят так, как будто они происходят во внутреннем компиляторе Razor (см. два из них ниже). У меня возникают проблемы при интерпретации этих ошибок,но я предполагаю, что способ, которым я вызываю компилятор Razor, не безопасен для параллелизма.

Является ли это известной проблемой с компилятором Razor? Как обычные представления Razor (.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;
    }

Ошибка 1:

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)

Ошибка 2:

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)

1 Ответ

16 голосов
/ 22 июня 2011

Обновление: Согласно сообщению в блоге от их команды, последняя версия 3.x ( на 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 - статический метод. DefaultTemplateService является статическим свойством Razor, а Parse и GetTemplate являются методами экземпляра, но эффективно вызываются статически из-за статического DefaultTemplateService. Это означает, что все потоки проходят через один и тот же экземпляр и проходят GetTemplate. Вы заметите, что GetTemplate изменяет состояние (templateCache) без получения каких-либо блокировок. Поэтому этот код не является потокобезопасным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...