2020-12-25 乐帮网
netcore
在本文中,将会介绍ASP.NET Core Razor如何将模板转换为程序集并运行。我们还会介绍的基于Razor的字符串模板引擎在ASP.NET外部使用的步骤。
主机内容:
了解ASP.NET Core Razor如何将模板转换为程序集并运行它们
逐步完成使我们的基于Razor的字符串模板引擎的步骤,以在ASP.NET之外使用。
Razor解析器的工作原理在此就不做介绍了。
在这之前你必须知道以下内容:Razor是ASP.NET MVC视图的模板引擎。它旨在将模型应用于模板以生成HTML页面。
HomeController.cs
public class HomeController : Controller
{
public IActionResult Index()
{
IndexModel model = new IndexModel()
{
Name = "Harry Harrison",
Novels = new List<Novel>()
{
new Novel()
{
Name = "Deathworld",
Year = 1960
},
new Novel()
{
Name = "Spaceship Medic",
Year = 1970
},
}
};
return this.View(model);
}
}
Index.cshtml
@model IndexModel
<h1>@Model.Name</h1>
<ul>
@foreach (Novel novel in Model.Novels)
{
<li>@novel.Year, @novel.Name</li>
}
</ul>
最终生成的静态html代码如下
<h1>Harry Harrison</h1>
<ul>
<li>1960, Deathworld</li>
<li>1970, Spaceship Medic</li>
</ul>
这个过程是怎么进行的呢?
1.模板解析
Razor模板已编译,因此首先Razor需要将字符串模板转换为C#代码。
我们将使用最新的ASP.NET Core Razor软件包: Microsoft.AspNetCore.Razor.Language
string GenerateCodeFromTemplate(string template)
{
RazorProjectEngine engine = RazorProjectEngine.Create(
RazorConfiguration.Default,
RazorProjectFileSystem.Create(@"."),
(builder) =>
{
builder.SetNamespace("MyNamespace");
});
string fileName = Path.GetRandomFileName();
RazorSourceDocument document = RazorSourceDocument.Create(template, fileName);
RazorCodeDocument codeDocument = engine.Process(
document,
null,
new List<RazorSourceDocument>(),
new List<TagHelperDescriptor>());
RazorCSharpDocument razorCSharpDocument = codeDocument.GetCSharpDocument();
return razorCSharpDocument.GeneratedCode;
}
调用GenerateCodeFromTemplate将得到实际的类源代码。
GenerateCodeFromTemplate("Hello @Model.Name")
namespace MyNamespace
{
public class Template
{
public async override global::System.Threading.Tasks.Task ExecuteAsync()
{
WriteLiteral("Hello ");
Write(Model.Name);
}
}
在我定义的Template ,类名将始终位于您选择的名称空间MyNamespace 下。
由于未定义WriteLiteral 和Write 函数,因此这些代码将无法编译,我们需要Template继承一些东西才能使其正常工作。
StringBuilder builder = new StringBuilder();
builder.AppendLine("@inherits ConsoleApp9.MyTemplateBase");
builder.Append(@"Hello @Model.Name");
Console.WriteLine(GenerateCodeFromTemplate(builder.ToString()));
现在我们有:
让我们定义MyTemplateBase准备编译模板。
它具有三个重要成员:
public dynamic Model { get; set; }; //–我们将在模板中使用该模型来引用数据
public abstract Task ExecuteAsync(); //–开始执行的模板入口点
public string Result();// –将来会取得成果的东西
public abstract class MyTemplateBase
{
private readonly StringBuilder stringBuilder = new StringBuilder();
public dynamic Model { get; set; }
public abstract Task ExecuteAsync();
public void WriteLiteral(string literal)
{
this.stringBuilder.Append(literal);
}
public void Write(object obj)
{
this.stringBuilder.Append(obj);
}
public string Result()
{
return this.stringBuilder.ToString();
}
}
2.编译
我们将使用Roslyn来编译此代码。包:Microsoft.CodeAnalysis.CSharp
为了使用Roslyn构建对象,您需要构建SyndexTree和引用程序集(就像在常规控制台应用程序中那样)。
static MemoryStream Compile(string assemblyName, string code)
{
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
new[]
{
syntaxTree
},
new []
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(MyTemplateBase).Assembly.Location),
MetadataReference.CreateFromFile(typeof(DynamicObject).Assembly.Location),
MetadataReference.CreateFromFile(
Assembly.Load(new AssemblyName("Microsoft.CSharp")).Location),
MetadataReference.CreateFromFile(
Assembly.Load(new AssemblyName("netstandard")).Location),
MetadataReference.CreateFromFile(
Assembly.Load(new AssemblyName("System.Runtime")).Location),
},
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
MemoryStream memoryStream = new MemoryStream();
EmitResult emitResult = compilation.Emit(memoryStream);
if (!emitResult.Success)
{
return null;
}
memoryStream.Position = 0;
return memoryStream;
}
开始运行,现在我们在内存流中有了汇编字节码!
3. 构建运行
让我们加载字节码。
Assembly assembly = Assembly.Load(memoryStream.ToArray());
Type templateType = assembly.GetType("MyNamespace.Template");
现在我们可以创建实例并尝试运行它。
MyTemplateBase instance = (MyTemplateBase) Activator.CreateInstance(templateType);
instance.Model = new
{
Name = "Harry Harrison"
};
instance.ExecuteAsync().Wait();
Console.WriteLine(instance.Result());
这将导致错误,因为无法立即访问匿名对象的属性。
'object' does not contain a definition for Name
为了克服这个问题,我们将基于使用包装器DynamicObject(为简洁起见,删除了数组和嵌套对象处理)。
public class AnonymousTypeWrapper : DynamicObject
{
private readonly object model;
public AnonymousTypeWrapper(object model)
{
this.model = model;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
PropertyInfo propertyInfo = this.model.GetType().GetProperty(binder.Name);
if (propertyInfo == null)
{
result = null;
return false;
}
result = propertyInfo.GetValue(this.model, null);
// nested objects and array handling goes here
// full code: https://github.com/adoconnection/RazorEngineCore/blob/master/
// RazorEngineCore/AnonymousTypeWrapper.cs
return true;
}
}
最后一部分是我们需要克服的重点:
MyTemplateBase instance = (MyTemplateBase)Activator.CreateInstance(templateType);
var model = new
{
Name = "Harry Harrison"
};
instance.Model = new AnonymousTypeWrapper(model);
instance.ExecuteAsync().Wait();
Console.WriteLine(instance.Result());
源代码和Nuget包
https://github.com/adoconnection/RazorEngineCore
Nuget: Install-Package RazorEngineCore
原文地址:这里
关注我的微信公众号
在公众号里留言交流
投稿邮箱:1052839972@qq.com
庭院深深深几许?杨柳堆烟,帘幕无重数。
玉勒雕鞍游冶处,楼高不见章台路。
雨横风狂三月暮。门掩黄昏,无计留春住。
泪眼问花花不语,乱红飞过秋千去。
如果感觉对您有帮助
欢迎向作者提供捐赠
这将是创作的最大动力