using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using RazorLight.Templating;
using RazorLight.Templating.FileSystem;
using RazorLight.Caching;
using RazorLight.Templating.Embedded;
using System.Diagnostics;
using System.Reflection;
using System.Linq;
using System.Collections.Concurrent;
using RazorLight.Host.Internal;
using RazorLight;
using System.IO;
using RazorLight.Compilation;
namespace Common
{
/// <summary>
/// RazorLight 模板帮助类
/// </summary>
public static class RazorLightCommon
{
private static IRazorLightEngine engine = null;
/// <summary>
/// Adds RazorLight services that resolve templates from a given <paramref name="root"/>
/// </summary>
/// <param name="services">Service collection</param>
/// <param name="root">Root views folder</param>
/// <param name="config">Configuration (can be null)</param>
public static void AddRazorLight(this IServiceCollection services, string root, Action<IEngineConfiguration> config = null)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (string.IsNullOrEmpty(root))
{
throw new ArgumentNullException(nameof(root));
}
if (root.StartsWith("/"))
{
root = root.Substring(1);
}
else if (root.StartsWith("~/"))
{
root = root.Substring(2);
}
//Setup configuration
var configuration = EngineConfiguration.Default;
config?.Invoke(configuration);
//Resolve root path
IServiceProvider serviceProvider = services.BuildServiceProvider();
IHostingEnvironment env = serviceProvider.GetService<IHostingEnvironment>();
string absoluteRootPath = System.IO.Path.Combine(env.ContentRootPath, root);
services.AddTransient<ITemplateManager>(p => new FilesystemTemplateManager(absoluteRootPath));
services.AddSingleton<ICompilerCache>(new TrackingCompilerCache(absoluteRootPath));
services.AddSingleton<IEngineConfiguration>(configuration);
services.AddSingleton<IEngineCore, EngineCore>();
services.AddSingleton<IPageFactoryProvider>(p => new CachingPageFactory(
p.GetService<IEngineCore>().KeyCompile,
p.GetService<ICompilerCache>()));
services.AddSingleton<IPageLookup, FilesystemPageLookup>();
services.AddSingleton<PropertyInjector>();
services.AddSingleton<IRazorLightEngine, RazorLightEngine>(p =>
{
var engine = new RazorLightEngine(
p.GetRequiredService<IEngineCore>(),
p.GetRequiredService<IPageLookup>());
AddEngineRenderCallbacks(engine, p);
return engine;
});
}
/// <summary>
/// Creates RazorLight services that resolves templates inside given type assembly as a EmbeddedResource
/// </summary>
/// <param name="services">Service collection</param>
/// <param name="rootType">Root type where</param>
/// <param name="config">Configuration (can be null)</param>
public static void AddRazorLight(this IServiceCollection services, Type rootType, Action<IEngineConfiguration> config = null)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (rootType == null)
{
throw new ArgumentNullException(nameof(services));
}
var configuration = EngineConfiguration.Default;
config?.Invoke(configuration);
services.AddSingleton<IEngineConfiguration>(configuration);
services.AddTransient<ITemplateManager>(p => new EmbeddedResourceTemplateManager(rootType));
services.AddSingleton<IEngineCore, EngineCore>();
services.AddSingleton<IPageFactoryProvider>(p => new DefaultPageFactory(p.GetService<IEngineCore>().KeyCompile));
services.AddSingleton<IPageLookup, DefaultPageLookup>();
services.AddSingleton<IRazorLightEngine, RazorLightEngine>();
}
private static void AddEngineRenderCallbacks(IRazorLightEngine engine, IServiceProvider services)
{
var injector = services.GetRequiredService<PropertyInjector>();
engine.Configuration.PreRenderCallbacks.Add(template => injector.Inject(template));
}
#region 解析视图文件(cshtml) 生成 String
/// <summary>
/// 解析视图文件(cshtml) 生成 String
/// </summary>
/// <typeparam name="TModel">泛型实体类<peparam>
/// <param name="hostingEnv">根节点对象</param>
/// <param name="controllerName">控制器</param>
/// <param name="actionName">动作(Action)</param>
/// <param name="model">实体</param>
/// <param name="htengine">IRazorLightEngine对象</param>
/// <param name="viewBag">viewBag</param>
/// <returns></returns>
public static String CreateStrByRazorView<TModel>(IHostingEnvironment hostingEnv, String controllerName, String actionName, TModel model, out IRazorLightEngine htengine, System.Dynamic.ExpandoObject viewBag = null)
{
String content = "";
string path = hostingEnv.WebRootPath;
Directory.SetCurrentDirectory(Directory.GetParent(path).FullName);
path = Directory.GetCurrentDirectory() + "\\views\\" + controllerName;
if (engine == null)
{
engine = EngineFactory.CreatePhysical(path);
}
if (viewBag == null)
{
content = engine.Parse<TModel>(actionName + ".cshtml", model);
}
else
{
content = engine.Parse<TModel>(actionName + ".cshtml", model, viewBag);
}
htengine = engine;
return content;
}
#endregion
/// <summary>
/// 解析Razor 字符串生成 字符串
/// </summary>
/// <typeparam name="TModel">泛型实体类</typeparam>
/// <param name="engine">IRazorLightEngine 对象</param>
/// <param name="content">Razor 字符串</param>
/// <param name="model">数据模型</param>
/// <returns></returns>
public static String CreateStrByRazorString<TModel>(IRazorLightEngine engine, String content, TModel model)
{
if (!string.IsNullOrWhiteSpace(content))
{
content = engine.ParseString<TModel>(content, model);
}
return content;
}
}
public class PropertyInjector
{
private readonly IServiceProvider services;
private readonly ConcurrentDictionary<PropertyInfo, FastPropertySetter> _propertyCache;
public PropertyInjector(IServiceProvider services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
this.services = services;
this._propertyCache = new ConcurrentDictionary<PropertyInfo, FastPropertySetter>();
}
public void Inject(TemplatePage page)
{
if (page == null)
{
throw new ArgumentNullException(nameof(page));
}
PropertyInfo[] properties = page.GetType().GetRuntimeProperties()
.Where(p =>
{
return
p.IsDefined(typeof(RazorInjectAttribute)) &&
p.GetIndexParameters().Length == 0 &&
!p.SetMethod.IsStatic;
}).ToArray();
foreach (var property in properties)
{
Type memberType = property.PropertyType;
object instance = services.GetRequiredService(memberType);
FastPropertySetter setter = _propertyCache.GetOrAdd(property, new FastPropertySetter(property));
setter.SetValue(page, instance);
}
}
}
public class FastPropertySetter
{
private static readonly MethodInfo CallPropertySetterOpenGenericMethod =
typeof(FastPropertySetter).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertySetter));
/// <summary>
/// Initializes a fast <see cref="FastPropertySetter"/>.
/// </summary>
public FastPropertySetter(PropertyInfo property)
{
if (property == null)
{
throw new ArgumentNullException(nameof(property));
}
Property = property;
Name = property.Name;
}
private Action<object, object> _valueSetter;
/// <summary>
/// Gets the backing <see cref="PropertyInfo"/>.
/// </summary>
public PropertyInfo Property { get; }
/// <summary>
/// Gets (or sets in derived types) the property name.
/// </summary>
public virtual string Name { get; protected set; }
/// <summary>
/// Gets the property value setter.
/// </summary>
public Action<object, object> ValueSetter
{
get
{
if (_valueSetter == null)
{
_valueSetter = MakeFastPropertySetter(Property);
}
return _valueSetter;
}
}
/// <summary>
/// Sets the property value for the specified <paramref name="instance" />.
/// </summary>
/// <param name="instance">The object whose property value will be set.</param>
/// <param name="value">The property value.</param>
public void SetValue(object instance, object value)
{
ValueSetter(instance, value);
}
/// <summary>
/// Creates a single fast property setter for reference types. The result is not cached.
/// </summary>
/// <param name="propertyInfo">propertyInfo to extract the setter for.</param>
/// <returns>a fast getter.</returns>
/// <remarks>
/// This method is more memory efficient than a dynamically compiled lambda, and about the
/// same speed. This only works for reference types.
/// </remarks>
public static Action<object, object> MakeFastPropertySetter(PropertyInfo propertyInfo)
{
if (propertyInfo == null)
{
throw new ArgumentNullException(nameof(propertyInfo));
}
if (propertyInfo.DeclaringType.GetTypeInfo().IsValueType)
{
throw new RazorLightException("Only reference types are allowed");
}
MethodInfo setMethod = propertyInfo.SetMethod;
Debug.Assert(setMethod != null);
Debug.Assert(!setMethod.IsStatic);
Debug.Assert(setMethod.ReturnType == typeof(void));
ParameterInfo[] parameters = setMethod.GetParameters();
Debug.Assert(parameters.Length == 1);
// Instance methods in the CLR can be turned into static methods where the first parameter
// is open over "target". This parameter is always passed by reference, so we have a code
// path for value types and a code path for reference types.
Type typeInput = setMethod.DeclaringType;
Type parameterType = parameters[0].ParameterType;
// Create a delegate TDeclaringType -> { TDeclaringType.Property = TValue; }
Delegate propertySetterAsAction =
setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeInput, parameterType));
MethodInfo callPropertySetterClosedGenericMethod =
CallPropertySetterOpenGenericMethod.MakeGenericMethod(typeInput, parameterType);
Delegate callPropertySetterDelegate =
callPropertySetterClosedGenericMethod.CreateDelegate(
typeof(Action<object, object>), propertySetterAsAction);
return (Action<object, object>)callPropertySetterDelegate;
}
private static void CallPropertySetter<TDeclaringType, TValue>(
Action<TDeclaringType, TValue> setter,
object target,
object value)
{
setter((TDeclaringType)target, (TValue)value);
}
}
/// <summary>
/// RazorLight 扩展类 扩展ParseString 方法
/// </summary>
public static class RazorLightExtensions
{
/// <summary>
/// Parses a string
/// </summary>
/// <typeparam name="T">Type of the model</typeparam>
/// <param name="content">Template to parse</param>
/// <param name="model">Template model</param>
/// <returns>Returns parsed string</returns>
/// <remarks>Result is not cached</remarks>
public static string ParseString<T>(this IRazorLightEngine engine, string content, T model)
{
return engine.ParseString(content, model, typeof(T));
}
/// <summary>
/// Parses a string
/// </summary>
/// <param name="content">Template to parse</param>
/// <param name="model">Template model</param>
/// <param name="modelType">Type of the model</param>
/// <returns></returns>
public static string ParseString(this IRazorLightEngine engine, string content, object model, Type modelType)
{
if (string.IsNullOrEmpty(content))
{
throw new ArgumentNullException(nameof(content));
}
ITemplateSource templateSource = new LoadedTemplateSource(content);
ModelTypeInfo modelTypeInfo = new ModelTypeInfo(modelType);
CompilationResult result = engine.Core.CompileSource(templateSource, modelTypeInfo);
result.EnsureSuccessful();
TemplatePage page = engine.Activate(result.CompiledType);
page.PageContext = new PageContext() { ModelTypeInfo = modelTypeInfo };
return engine.RunTemplate(page, model);
}
}
#region 使用说明
// 1,导包
//PM> Install-Package RazorLight
// 2,网站的 Startup 类下 实现注入
// public void ConfigureServices(IServiceCollection services)
// {
// services.AddRazorLight("/Views");
// }
//public class KindEditorController : Controller
//{
// private IHostingEnvironment hostingEnv;
// public KindEditorController(IHostingEnvironment env)
// {
// this.hostingEnv = env;
// }
// public String Test()
// {
// RazorLightCommon.CreateStrByView<Test>(hostingEnv, "Test", "index", null,"");
// }
//}
#endregion
}
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using RazorLight.Templating;
using RazorLight.Templating.FileSystem;
using RazorLight.Caching;
using RazorLight.Templating.Embedded;
using System.Diagnostics;
using System.Reflection;
using System.Linq;
using System.Collections.Concurrent;
using RazorLight.Host.Internal;
using RazorLight;
using System.IO;
using RazorLight.Compilation;
namespace Common
{
/// <summary>
/// RazorLight 模板帮助类
/// </summary>
public static class RazorLightCommon
{
private static IRazorLightEngine engine = null;
/// <summary>
/// Adds RazorLight services that resolve templates from a given <paramref name="root"/>
/// </summary>
/// <param name="services">Service collection</param>
/// <param name="root">Root views folder</param>
/// <param name="config">Configuration (can be null)</param>
public static void AddRazorLight(this IServiceCollection services, string root, Action<IEngineConfiguration> config = null)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (string.IsNullOrEmpty(root))
{
throw new ArgumentNullException(nameof(root));
}
if (root.StartsWith("/"))
{
root = root.Substring(1);
}
else if (root.StartsWith("~/"))
{
root = root.Substring(2);
}
//Setup configuration
var configuration = EngineConfiguration.Default;
config?.Invoke(configuration);
//Resolve root path
IServiceProvider serviceProvider = services.BuildServiceProvider();
IHostingEnvironment env = serviceProvider.GetService<IHostingEnvironment>();
string absoluteRootPath = System.IO.Path.Combine(env.ContentRootPath, root);
services.AddTransient<ITemplateManager>(p => new FilesystemTemplateManager(absoluteRootPath));
services.AddSingleton<ICompilerCache>(new TrackingCompilerCache(absoluteRootPath));
services.AddSingleton<IEngineConfiguration>(configuration);
services.AddSingleton<IEngineCore, EngineCore>();
services.AddSingleton<IPageFactoryProvider>(p => new CachingPageFactory(
p.GetService<IEngineCore>().KeyCompile,
p.GetService<ICompilerCache>()));
services.AddSingleton<IPageLookup, FilesystemPageLookup>();
services.AddSingleton<PropertyInjector>();
services.AddSingleton<IRazorLightEngine, RazorLightEngine>(p =>
{
var engine = new RazorLightEngine(
p.GetRequiredService<IEngineCore>(),
p.GetRequiredService<IPageLookup>());
AddEngineRenderCallbacks(engine, p);
return engine;
});
}
/// <summary>
/// Creates RazorLight services that resolves templates inside given type assembly as a EmbeddedResource
/// </summary>
/// <param name="services">Service collection</param>
/// <param name="rootType">Root type where</param>
/// <param name="config">Configuration (can be null)</param>
public static void AddRazorLight(this IServiceCollection services, Type rootType, Action<IEngineConfiguration> config = null)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (rootType == null)
{
throw new ArgumentNullException(nameof(services));
}
var configuration = EngineConfiguration.Default;
config?.Invoke(configuration);
services.AddSingleton<IEngineConfiguration>(configuration);
services.AddTransient<ITemplateManager>(p => new EmbeddedResourceTemplateManager(rootType));
services.AddSingleton<IEngineCore, EngineCore>();
services.AddSingleton<IPageFactoryProvider>(p => new DefaultPageFactory(p.GetService<IEngineCore>().KeyCompile));
services.AddSingleton<IPageLookup, DefaultPageLookup>();
services.AddSingleton<IRazorLightEngine, RazorLightEngine>();
}
private static void AddEngineRenderCallbacks(IRazorLightEngine engine, IServiceProvider services)
{
var injector = services.GetRequiredService<PropertyInjector>();
engine.Configuration.PreRenderCallbacks.Add(template => injector.Inject(template));
}
#region 解析视图文件(cshtml) 生成 String
/// <summary>
/// 解析视图文件(cshtml) 生成 String
/// </summary>
/// <typeparam name="TModel">泛型实体类<peparam>
/// <param name="hostingEnv">根节点对象</param>
/// <param name="controllerName">控制器</param>
/// <param name="actionName">动作(Action)</param>
/// <param name="model">实体</param>
/// <param name="htengine">IRazorLightEngine对象</param>
/// <param name="viewBag">viewBag</param>
/// <returns></returns>
public static String CreateStrByRazorView<TModel>(IHostingEnvironment hostingEnv, String controllerName, String actionName, TModel model, out IRazorLightEngine htengine, System.Dynamic.ExpandoObject viewBag = null)
{
String content = "";
string path = hostingEnv.WebRootPath;
Directory.SetCurrentDirectory(Directory.GetParent(path).FullName);
path = Directory.GetCurrentDirectory() + "\\views\\" + controllerName;
if (engine == null)
{
engine = EngineFactory.CreatePhysical(path);
}
if (viewBag == null)
{
content = engine.Parse<TModel>(actionName + ".cshtml", model);
}
else
{
content = engine.Parse<TModel>(actionName + ".cshtml", model, viewBag);
}
htengine = engine;
return content;
}
#endregion
/// <summary>
/// 解析Razor 字符串生成 字符串
/// </summary>
/// <typeparam name="TModel">泛型实体类</typeparam>
/// <param name="engine">IRazorLightEngine 对象</param>
/// <param name="content">Razor 字符串</param>
/// <param name="model">数据模型</param>
/// <returns></returns>
public static String CreateStrByRazorString<TModel>(IRazorLightEngine engine, String content, TModel model)
{
if (!string.IsNullOrWhiteSpace(content))
{
content = engine.ParseString<TModel>(content, model);
}
return content;
}
}
public class PropertyInjector
{
private readonly IServiceProvider services;
private readonly ConcurrentDictionary<PropertyInfo, FastPropertySetter> _propertyCache;
public PropertyInjector(IServiceProvider services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
this.services = services;
this._propertyCache = new ConcurrentDictionary<PropertyInfo, FastPropertySetter>();
}
public void Inject(TemplatePage page)
{
if (page == null)
{
throw new ArgumentNullException(nameof(page));
}
PropertyInfo[] properties = page.GetType().GetRuntimeProperties()
.Where(p =>
{
return
p.IsDefined(typeof(RazorInjectAttribute)) &&
p.GetIndexParameters().Length == 0 &&
!p.SetMethod.IsStatic;
}).ToArray();
foreach (var property in properties)
{
Type memberType = property.PropertyType;
object instance = services.GetRequiredService(memberType);
FastPropertySetter setter = _propertyCache.GetOrAdd(property, new FastPropertySetter(property));
setter.SetValue(page, instance);
}
}
}
public class FastPropertySetter
{
private static readonly MethodInfo CallPropertySetterOpenGenericMethod =
typeof(FastPropertySetter).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertySetter));
/// <summary>
/// Initializes a fast <see cref="FastPropertySetter"/>.
/// </summary>
public FastPropertySetter(PropertyInfo property)
{
if (property == null)
{
throw new ArgumentNullException(nameof(property));
}
Property = property;
Name = property.Name;
}
private Action<object, object> _valueSetter;
/// <summary>
/// Gets the backing <see cref="PropertyInfo"/>.
/// </summary>
public PropertyInfo Property { get; }
/// <summary>
/// Gets (or sets in derived types) the property name.
/// </summary>
public virtual string Name { get; protected set; }
/// <summary>
/// Gets the property value setter.
/// </summary>
public Action<object, object> ValueSetter
{
get
{
if (_valueSetter == null)
{
_valueSetter = MakeFastPropertySetter(Property);
}
return _valueSetter;
}
}
/// <summary>
/// Sets the property value for the specified <paramref name="instance" />.
/// </summary>
/// <param name="instance">The object whose property value will be set.</param>
/// <param name="value">The property value.</param>
public void SetValue(object instance, object value)
{
ValueSetter(instance, value);
}
/// <summary>
/// Creates a single fast property setter for reference types. The result is not cached.
/// </summary>
/// <param name="propertyInfo">propertyInfo to extract the setter for.</param>
/// <returns>a fast getter.</returns>
/// <remarks>
/// This method is more memory efficient than a dynamically compiled lambda, and about the
/// same speed. This only works for reference types.
/// </remarks>
public static Action<object, object> MakeFastPropertySetter(PropertyInfo propertyInfo)
{
if (propertyInfo == null)
{
throw new ArgumentNullException(nameof(propertyInfo));
}
if (propertyInfo.DeclaringType.GetTypeInfo().IsValueType)
{
throw new RazorLightException("Only reference types are allowed");
}
MethodInfo setMethod = propertyInfo.SetMethod;
Debug.Assert(setMethod != null);
Debug.Assert(!setMethod.IsStatic);
Debug.Assert(setMethod.ReturnType == typeof(void));
ParameterInfo[] parameters = setMethod.GetParameters();
Debug.Assert(parameters.Length == 1);
// Instance methods in the CLR can be turned into static methods where the first parameter
// is open over "target". This parameter is always passed by reference, so we have a code
// path for value types and a code path for reference types.
Type typeInput = setMethod.DeclaringType;
Type parameterType = parameters[0].ParameterType;
// Create a delegate TDeclaringType -> { TDeclaringType.Property = TValue; }
Delegate propertySetterAsAction =
setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeInput, parameterType));
MethodInfo callPropertySetterClosedGenericMethod =
CallPropertySetterOpenGenericMethod.MakeGenericMethod(typeInput, parameterType);
Delegate callPropertySetterDelegate =
callPropertySetterClosedGenericMethod.CreateDelegate(
typeof(Action<object, object>), propertySetterAsAction);
return (Action<object, object>)callPropertySetterDelegate;
}
private static void CallPropertySetter<TDeclaringType, TValue>(
Action<TDeclaringType, TValue> setter,
object target,
object value)
{
setter((TDeclaringType)target, (TValue)value);
}
}
/// <summary>
/// RazorLight 扩展类 扩展ParseString 方法
/// </summary>
public static class RazorLightExtensions
{
/// <summary>
/// Parses a string
/// </summary>
/// <typeparam name="T">Type of the model</typeparam>
/// <param name="content">Template to parse</param>
/// <param name="model">Template model</param>
/// <returns>Returns parsed string</returns>
/// <remarks>Result is not cached</remarks>
public static string ParseString<T>(this IRazorLightEngine engine, string content, T model)
{
return engine.ParseString(content, model, typeof(T));
}
/// <summary>
/// Parses a string
/// </summary>
/// <param name="content">Template to parse</param>
/// <param name="model">Template model</param>
/// <param name="modelType">Type of the model</param>
/// <returns></returns>
public static string ParseString(this IRazorLightEngine engine, string content, object model, Type modelType)
{
if (string.IsNullOrEmpty(content))
{
throw new ArgumentNullException(nameof(content));
}
ITemplateSource templateSource = new LoadedTemplateSource(content);
ModelTypeInfo modelTypeInfo = new ModelTypeInfo(modelType);
CompilationResult result = engine.Core.CompileSource(templateSource, modelTypeInfo);
result.EnsureSuccessful();
TemplatePage page = engine.Activate(result.CompiledType);
page.PageContext = new PageContext() { ModelTypeInfo = modelTypeInfo };
return engine.RunTemplate(page, model);
}
}
#region 使用说明
// 1,导包
//PM> Install-Package RazorLight
// 2,网站的 Startup 类下 实现注入
// public void ConfigureServices(IServiceCollection services)
// {
// services.AddRazorLight("/Views");
// }
//public class KindEditorController : Controller
//{
// private IHostingEnvironment hostingEnv;
// public KindEditorController(IHostingEnvironment env)
// {
// this.hostingEnv = env;
// }
// public String Test()
// {
// RazorLightCommon.CreateStrByView<Test>(hostingEnv, "Test", "index", null,"");
// }
//}
#endregion
}