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();
}
}
}