using NCC.DataEncryption; using NCC.Dependency; using Microsoft.AspNetCore.Razor.Language; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Text; using System.Threading.Tasks; namespace NCC.ViewEngine { /// /// 视图引擎实现类 /// [SuppressSniffer] public class ViewEngine : IViewEngine { /// /// 编译并运行 /// /// /// /// /// public string RunCompile(string content, object model = null, Action builderAction = null) { var template = Compile(content, builderAction); var result = template.Run(model); return result; } /// /// 编译并运行 /// /// /// /// /// public async Task RunCompileAsync(string content, object model = null, Action builderAction = null) { var template = await CompileAsync(content, builderAction); var result = await template.RunAsync(model); return result; } /// /// 编译并运行 /// /// /// /// /// /// public string RunCompile(string content, T model, Action builderAction = null) where T : class, new() { var template = Compile>(content, builderAction); var result = template.Run(u => { u.Model = model; }); return result; } /// /// 编译并运行 /// /// /// /// /// /// public async Task RunCompileAsync(string content, T model, Action builderAction = null) where T : class, new() { var template = await CompileAsync>(content, builderAction); var result = await template.RunAsync(u => { u.Model = model; }); return result; } /// /// 通过缓存解析模板 /// /// /// /// /// /// public string RunCompileFromCached(string content, object model = null, string cacheFileName = default, Action builderAction = null) { var fileName = cacheFileName ?? MD5Encryption.Encrypt(content); IViewEngineTemplate template; if (File.Exists(Penetrates.GetTemplateFileName(fileName))) template = ViewEngineTemplate.LoadFromFile(fileName); else { template = Compile(content, builderAction); template.SaveToFile(fileName); } var result = template.Run(model); return result; } /// /// 通过缓存解析模板 /// /// /// /// /// /// public async Task RunCompileFromCachedAsync(string content, object model = null, string cacheFileName = default, Action builderAction = null) { var fileName = cacheFileName ?? MD5Encryption.Encrypt(content); IViewEngineTemplate template; if (File.Exists(Penetrates.GetTemplateFileName(fileName))) template = await ViewEngineTemplate.LoadFromFileAsync(fileName); else { template = await CompileAsync(content, builderAction); await template.SaveToFileAsync(fileName); } var result = await template.RunAsync(model); return result; } /// /// 通过缓存解析模板 /// /// /// /// /// /// /// public string RunCompileFromCached(string content, T model, string cacheFileName = default, Action builderAction = null) where T : class, new() { var fileName = cacheFileName ?? MD5Encryption.Encrypt(content); IViewEngineTemplate> template; if (File.Exists(Penetrates.GetTemplateFileName(fileName))) template = ViewEngineTemplate>.LoadFromFile(fileName); else { template = Compile>(content, builderAction); template.SaveToFile(fileName); } var result = template.Run(u => { u.Model = model; }); return result; } /// /// 通过缓存解析模板 /// /// /// /// /// /// public async Task RunCompileFromCachedAsync(string content, T model, string cacheFileName = default, Action builderAction = null) where T : class, new() { var fileName = cacheFileName ?? MD5Encryption.Encrypt(content); IViewEngineTemplate> template; if (File.Exists(Penetrates.GetTemplateFileName(fileName))) template = await ViewEngineTemplate>.LoadFromFileAsync(fileName); else { template = await CompileAsync>(content, builderAction); await template.SaveToFileAsync(fileName); } var result = await template.RunAsync(u => { u.Model = model; }); return result; } /// /// 编译模板 /// /// /// /// public IViewEngineTemplate Compile(string content, Action builderAction = null) { IViewEngineOptionsBuilder compilationOptionsBuilder = new ViewEngineOptionsBuilder(); compilationOptionsBuilder.Inherits(typeof(ViewEngineModel)); builderAction?.Invoke(compilationOptionsBuilder); var memoryStream = CreateAndCompileToStream(content, compilationOptionsBuilder.Options); return new ViewEngineTemplate(memoryStream); } /// /// 编译模板 /// /// /// /// public Task CompileAsync(string content, Action builderAction = null) { return Task.Factory.StartNew(() => Compile(content: content, builderAction: builderAction)); } /// /// 编译模板 /// /// /// /// /// public IViewEngineTemplate Compile(string content, Action builderAction = null) where T : IViewEngineModel { IViewEngineOptionsBuilder compilationOptionsBuilder = new ViewEngineOptionsBuilder(); compilationOptionsBuilder.AddAssemblyReference(typeof(T).Assembly); compilationOptionsBuilder.Inherits(typeof(T)); builderAction?.Invoke(compilationOptionsBuilder); var memoryStream = CreateAndCompileToStream(content, compilationOptionsBuilder.Options); return new ViewEngineTemplate(memoryStream); } /// /// 编译模板 /// /// /// /// /// public Task> CompileAsync(string content, Action builderAction = null) where T : IViewEngineModel { return Task.Factory.StartNew(() => Compile(content: content, builderAction: builderAction)); } /// /// 将模板内容编译并输出内存流 /// /// /// /// private static MemoryStream CreateAndCompileToStream(string templateSource, ViewEngineOptions options) { templateSource = WriteDirectives(templateSource, options); var engine = RazorProjectEngine.Create( RazorConfiguration.Default, RazorProjectFileSystem.Create(@"."), (builder) => { builder.SetNamespace(options.TemplateNamespace); }); var fileName = Path.GetRandomFileName(); var document = RazorSourceDocument.Create(templateSource, fileName); var codeDocument = engine.Process( document, null, new List(), new List()); var razorCSharpDocument = codeDocument.GetCSharpDocument(); var syntaxTree = CSharpSyntaxTree.ParseText(razorCSharpDocument.GeneratedCode); var compilation = CSharpCompilation.Create( fileName, new[] { syntaxTree }, options.ReferencedAssemblies .Select(ass => { // MetadataReference.CreateFromFile(ass.Location) unsafe { ass.TryGetRawMetadata(out var blob, out var length); var moduleMetadata = ModuleMetadata.CreateFromMetadata((IntPtr)blob, length); var assemblyMetadata = AssemblyMetadata.Create(moduleMetadata); var metadataReference = assemblyMetadata.GetReference(); return metadataReference; } }) .Concat(options.MetadataReferences) .ToList(), new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); var memoryStream = new MemoryStream(); var emitResult = compilation.Emit(memoryStream); if (!emitResult.Success) { var exception = new ViewEngineTemplateException() { Errors = emitResult.Diagnostics.ToList(), GeneratedCode = razorCSharpDocument.GeneratedCode }; throw exception; } memoryStream.Position = 0; return memoryStream; } /// /// 写入Razor 命令 /// /// /// /// private static string WriteDirectives(string content, ViewEngineOptions options) { var stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"@inherits {options.Inherits}"); foreach (var entry in options.DefaultUsings) { stringBuilder.AppendLine($"@using {entry}"); } stringBuilder.Append(content); return stringBuilder.ToString(); } } }