通常我们有:
<div>
<div>Some property from a model: @Model.Property</div>
</div>
但是,让我说我在资源文件中将这条确切的行作为完整的html行并尝试引用它:
<div>
@Resource.MyResourceLine
</div>
模型绑定不起作用。它渲染生成线而不绑定。
如何在这种情况下使Razor绑定?
编辑: 通过将资源字符串上的内容更改为string.Format占位符,可以采用另一种方法:
<div>Some property from a model:{0}</div>
然后:
<div>
@string.Format(@Resource.MyResourceLine,@Model.Property)
</div>
但这使得很难维护包含许多属性引用的大型文本。如果可以在资源文件中看到属性名称,那将是理想的。有更优雅的方式吗?
答案 0 :(得分:1)
我在官方codeplex中搜索的Asp.Net Mvc(最新版本为5.2.3)的源代码中进行了一些挖掘:https://aspnetwebstack.codeplex.com/
简短回答: 开箱即用没有简单的方法,因为页面已经被编译,并且您在模型中传递的任何字符串都被视为字符串 - MvcHtmlString或String。您可以使用RazorEngine软件包快速完成并且没有太多问题:(https://github.com/Antaris/RazorEngine)
答案很长:
当你打开路径,并且控制器为它提供视图时,你必须为该视图获取已解析和编译的代码(可能在启动期间生成,或者在实际使用该视图之前懒洋洋地生成),然后渲染将已编译的View和模型数据组合在一起的页面(在控制器中调用View()
方法时完成)。
ASP.NET如何解析和编译视图,为其生成运行代码:
// https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Razor/RazorTemplateEngine.cs
// Line 152
protected internal virtual GeneratorResults GenerateCodeCore(ITextDocument input, string className, string rootNamespace, string sourceFileName, CancellationToken? cancelToken) {
//...
// Run the parser
RazorParser parser = CreateParser();
Debug.Assert(parser != null);
ParserResults results = parser.Parse(input);
// Generate code
RazorCodeGenerator generator = CreateCodeGenerator(className, rootNamespace, sourceFileName);
generator.DesignTimeMode = Host.DesignTimeMode;
generator.Visit(results);
//...
}
asp.net如何呈现页面,结合视图的源代码和模型中的数据
// https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.WebPages/WebPageBase.cs
// Line 215
// public override void ExecutePageHierarchy() {
// ...
try
{
// Execute the developer-written code of the WebPage
Execute(); //**you can see example of the code it executes right below in the code block**
}
finally
{
TemplateStack.Pop(Context);
}
}
编译视图后,它变成了一个简单的C#类,它生成一个字符串,然后在浏览器中显示给用户。让我们创建一个简单的Controller,View和ViewModel:
以下是一些代码:
ViewModel类:
namespace StackOverflow.Models
{
public class TestViewModel
{
public int IntProperty { get; set; }
public string StringProperty { get; set; }
}
}
控制器代码:
public ActionResult Test()
{
var viewModel = new TestViewModel
{
IntProperty = 5,
StringProperty = "@DateTime.UtcNow.ToString()"
};
return View(viewModel);
}
查看:
@model StackOverflow.Models.TestViewModel
@{
ViewBag.Title = "just a test";
Layout = null;
}
@Model.IntProperty
@Html.Raw(@Model.StringProperty)
使用上面代码的页面示例生成以下编译视图:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ASP {
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Helpers;
using System.Web.Security;
using System.Web.UI;
using System.Web.WebPages;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using System.Web.Mvc.Html;
using System.Web.Optimization;
using System.Web.Routing;
using StackOverflow;
public class _Page_Views_Test_Index_cshtml : System.Web.Mvc.WebViewPage<StackOverflow.Models.TestViewModel> {
#line hidden
public _Page_Views_Test_Index_cshtml() {
}
protected ASP.global_asax ApplicationInstance {
get {
return ((ASP.global_asax)(Context.ApplicationInstance));
}
}
public override void Execute() {
#line 3 "XXX\Views\Test\Index.cshtml"
ViewBag.Title = "just a test";
Layout = null;
#line default
#line hidden
BeginContext("~/Views/Test/Index.cshtml", 100, 4, true);
WriteLiteral("\r\n\r\n");
EndContext("~/Views/Test/Index.cshtml", 100, 4, true);
BeginContext("~/Views/Test/Index.cshtml", 105, 17, false);
#line 8 "XXX\Views\Test\Index.cshtml"
Write(Model.IntProperty);
#line default
#line hidden
EndContext("~/Views/Test/Index.cshtml", 105, 17, false);
BeginContext("~/Views/Test/Index.cshtml", 122, 4, true);
WriteLiteral("\r\n\r\n");
EndContext("~/Views/Test/Index.cshtml", 122, 4, true);
BeginContext("~/Views/Test/Index.cshtml", 127, 31, false);
#line 10 "XXX\Views\Test\Index.cshtml"
Write(Html.Raw(@Model.StringProperty));
#line default
#line hidden
EndContext("~/Views/Test/Index.cshtml", 127, 31, false);
}
}
}
如您所见,您的页面代码只是逐节写入输出,检查Write
方法会导致这些实现细节:
public override void Write(object value)
{
WriteTo(Output, value);
}
public static void WriteTo(TextWriter writer, object content)
{
writer.Write(HttpUtility.HtmlEncode(content)); //writer - instance of TextWriter
}
因此,您在viewmodel中放入字符串字段的任何内容都只是使用HtmlEncode方法进行编码并放到页面中,并且无法在运行时使用默认使用的mvc功能进行编译。
我很确定,你可以用Mvc和Razor深入挖掘资源,但这需要更多的时间,可能需要很多好的老黑客。要获得快速简单的解决方案,您可以使用https://github.com/Antaris/RazorEngine包。您还可以查看其源代码,了解他们是如何做到的。
这是控制器代码,它将使用RazorEngine包提供模板:
public ActionResult Test()
{
var stringTemplate = @"
@model StackOverflow.Models.TestViewModel
<br/>
>>COMPILED
<br/>
@DateTime.UtcNow.ToString()
<br/>
Compiled model int property value:
<br/>
@Model.IntProperty
";
var viewModel = new TestViewModel
{
IntProperty = 5,
StringProperty = null
};
viewModel.StringProperty = Engine.Razor.RunCompile(stringTemplate, viewModel.GetType().ToString(), null, viewModel);
return View("Index", viewModel);
}
这里的基本思路非常简单 - 将渲染传递给组件,在控制器中执行并将您获得的字符串传递给ViewModel,然后使用@ HtmlHelper.Raw呈现从引擎获取的HTML。
这可能适用于很多场景,但我强烈建议你不要这样做,除非你真的需要它并且没有可行的选择。动态剃刀模板很难维护。