【问题标题】:Razor in 'custom environment' doesn't accept @model directive“自定义环境”中的 Razor 不接受 @model 指令
【发布时间】:2011-04-27 15:13:16
【问题描述】:

我正在尝试在沙盒环境中解析和编译 Razor 模板,也就是 自定义主机,基于 this 信息(架构见下文)。

我无法让智能感知正常工作,因此我指定了 BuildProvider,如 here 所述,并遵循该问题的答案中提供的“workaround”。

@model MyAssembly.MyModel intellisense 出现以下错误:

无法加载文件或程序集“System.Web.WebPages.Razor”或其依赖项之一。

(程序集引用并复制到本地,以及所有其他相关的 Razor 程序集)

在解析和编译模板时会抛出以下错误:

Line: 33 Col: 7 Error: name 'model' 在当前上下文中不存在

有什么线索/建议吗?

附言如果我删除 @model 指令,模板会解析并编译得很好

架构:

  • Web 应用程序:引用类库并使用 3d 方类库中的模型提供 .cshtml 模板文件。
  • 类库:包含 RazorHost 和 BaseTemplate 并引用 3d 方库以将模型添加到 web 应用程序提供的 .cshtml 文件。
  • 3d 派对类:为 web 应用提供模型

【问题讨论】:

    标签: .net razor


    【解决方案1】:

    @model 是 MVC 的 Razor 实现所特有的。因此,开箱即用,它不起作用。我已经向 codeplex 上的 RazorEngine 上传了一个补丁,它为其引擎添加了@model 支持,并且在该特定版本之外实现它非常容易。 http://razorengine.codeplex.com/SourceControl/list/patches

    它基本上涉及覆盖 razor 用于生成其类文件的 CodeGenerator 并覆盖 TryVisitSpecialSpan

    protected override bool TryVisitSpecialSpan(Span span) {
        return TryVisit<ModelSpan>(span, VisitModelSpan); 
          //This is where you would add more special span tests 
          //|| TryVisit<SomeOtherSpan>(span, Method);
    }
    
    void VisitModelSpan(ModelSpan span) {
        string modelName = span.ModelTypeName;
    
        if (DesignTimeMode) {
            WriteHelperVariable(span.Content, "__modelHelper");
        }
    }
    

    那么你还必须创建自己的 CSharpCodeParser

        public class CSharpRazorCodeParser : CSharpCodeParser {
            public string TypeName { get; set; }
    
            public CSharpRazorCodeParser() {
                RazorKeywords.Add("model", WrapSimpleBlockParser(System.Web.Razor.Parser.SyntaxTree.BlockType.Directive, ParseModelStatement));
            }
    
            bool ParseModelStatement(CodeBlockInfo block) {
                End(MetaCodeSpan.Create);
    
                SourceLocation endModelLocation = CurrentLocation;
    
                Context.AcceptWhiteSpace(includeNewLines: false);
    
                if (ParserHelpers.IsIdentifierStart(CurrentCharacter)) {
                    using (Context.StartTemporaryBuffer()) {
                        AcceptTypeName();
                        Context.AcceptTemporaryBuffer();
                    }
                } else {
                    OnError(endModelLocation, "Model Keyword Must Be Followed By Type Name");
                }
    
                End(ModelSpan.Create(Context, TypeName));
    
                return false;
            }
        }
    

    即使在那之后,您也必须覆盖主机才能使用您的新类

    public class RazorEngineHost : System.Web.Razor.RazorEngineHost {
    
        public RazorEngineHost(RazorCodeLanguage codeLanguage, Func<MarkupParser> markupParserFactory)
            : base(codeLanguage, markupParserFactory) { }
    
        public override System.Web.Razor.Generator.RazorCodeGenerator DecorateCodeGenerator(System.Web.Razor.Generator.RazorCodeGenerator generator) {
            if (generator is CSharpRazorCodeGenerator) {
                return new CSharpRazorCodeGenerator(generator.ClassName,
                                                       generator.RootNamespaceName,
                                                       generator.SourceFileName,
                                                       generator.Host, false);
            }
    
            return base.DecorateCodeGenerator(generator);
        }
    
        public override ParserBase DecorateCodeParser(ParserBase incomingCodeParser) {
            if (incomingCodeParser is CSharpCodeParser) {
                return new CSharpRazorCodeParser();
            } else {
                return base.DecorateCodeParser(incomingCodeParser);
            }
        }
    }
    

    您还必须创建自己的自定义 CodeSpan

    public class ModelSpan : CodeSpan {
        public ModelSpan(SourceLocation start, string content, string modelTypeName) : base(start, content) {
            this.ModelTypeName = modelTypeName;
        }
    
        public string ModelTypeName { get; private set; }
    
        public override int GetHashCode() {
            return base.GetHashCode() ^ (ModelTypeName ?? String.Empty).GetHashCode();
        }
    
        public override bool Equals(object obj) {
            ModelSpan span = obj as ModelSpan;
            return span != null && Equals(span);
        }
    
        private bool Equals(ModelSpan span) {
            return base.Equals(span) && string.Equals(ModelTypeName, span.ModelTypeName, StringComparison.Ordinal);
        }
    
        public new static ModelSpan Create(ParserContext context, string modelTypeName) {
            return new ModelSpan(context.CurrentSpanStart, context.ContentBuffer.ToString(), modelTypeName);
        }
    }
    

    这个实现除了告诉设计者使用什么模型之外没有做任何事情。它根本不应该影响编译,但允许编译器忽略这个特定的命令。

    【讨论】:

    • 这确实适用于设计时间,非常感谢。但是在运行时会引发相同的错误:Line: 33 Col: 7 Error: The name 'model' does not exist in the current context。现在想起来,模型没有传递到任何地方的视图......应该怎么做?
    • 确保您在代码中覆盖了您需要覆盖的所有内容,以使该代码成为被调用的代码,而不是默认的 razor 主机等等。您可以在 razor-engine.com 下载 RazorEngine 代码,并查看该代码如何与您的代码进行比较。如果没有看到你的实现,就很难猜出错误在哪里。
    • 酷,我目前正在撰写一篇博文以将其发布 - 尽管我一直很懒 :)
    • 如果你能完成那篇博文就好了……我已经使用了代码,效果很好,只是你忘记在代码解析器中设置 TypeName。很高兴知道为什么 ParseModelStatement 的返回值总是错误的,它被微软记录得很糟糕。否则绝对是优秀的代码示例,并且可以像宣传的那样工作。
    • 是否可以在派生主机中更改模板的 DefaultBaseType?我解析我的模板两次以获取模型,然后在提取模板基类型后再次解析 - 这显然非常低效。
    【解决方案2】:

    There is a simple solution for IntelliSense to work 带有“自定义 Razor 环境”和 Resharper(例如,使用 RazorEngine 进行报告)。要启用 IntelliSense,请在您的项目中创建以下类:

    public class EnableIntelliSenseFor<T> 
    {
      public readonly T Model;
    }
    

    在 .cshtml 文件的顶部添加以下行:

    @inherits EnableIntelliSenseFor<YourModelType>
    

    然后在解析模板时,只需按照 ThiagoPXP 的建议删除顶行:

    template = RemoveInheritsDirective(template);            
    var html = Razor.Parse(template, model);     
    
    private static string RemoveInheritsDirective(string template)
    {
       return template.StartsWith("@inherits") 
          ? template.Substring(template.IndexOf('\n') + 1) 
          : template;
    }
    

    使用@Model 访问您的模型,IntelliSense 应该会按预期工作。

    【讨论】:

      【解决方案3】:

      我有同样的问题,我的解决方案很简单。

      在文件的开头有@model 是很好的,因为它给了我们一些智能感知。但是,它破坏了剃刀引擎,所以我的解决方案是在调用解析器之前在运行时删除模型声明。

      string template = File.ReadAllText(@"C\myRazorView.cshtml");
      
      var model = new MyViewModel { Name = "Foo", Surname = "Bar" };
      
      //remove model declaration from the view file
      template = template.Replace("@model MyViewModel", "");
      string result = Razor.Parse(template, model);
      

      在我的情况下,所有视图都使用相同的模型,因此在第一行中使用 String.Replace() 对我来说很有效。

      您可以使用正则表达式或其他方式来增强它删除第一行。

      【讨论】:

        【解决方案4】:

        对我来说这很有效(在 NET4.0 上使用 RazorEngine 3.3):

        1.这一行到cshtml的顶部

        @inherits RazorEngine.Templating.TemplateBase<MyModel>
        

        2.Page_Load中的this

        var templateName = System.IO.Path.ChangeExtension( Request.PhysicalPath, "cshtml");
        var template = System.IO.File.ReadAllText(templateName);
        var r = Razor.Parse<MyModel>(template, new MyModel {
                        FileName = "Example.pdf",
                        MessageId = Guid.NewGuid()
                    }, "MyPage");
        Response.Write(r);
        

        【讨论】:

          猜你喜欢
          • 2018-11-20
          • 2013-08-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-12-18
          • 2023-04-04
          • 2012-09-13
          相关资源
          最近更新 更多