在开头也是先给大家道个歉,由于最近准备婚事导致这篇文章耽误了许久,同时也谢谢老婆大人对我的支持。
回顾上篇文章,我们重造了一个controller,这个controller中用到了视图引擎,我们的视图引擎虽然也叫Razor,但此Razor非mvc中的Razor,MVC中的Razor同样依赖于HttpContext,我们实现的Razor借用 RazorEngine。关于RazorEngine的更多介绍请参阅http://antaris.github.io/RazorEngine/。
在上篇文章中无论是View方法还是PartialView方法,都用到了CompileView对象,我们先来看一下CompileView类的实现。
/// <summary> /// 视图编译类 /// </summary> public class CompileView { private static Regex layoutEx = new Regex("Layout\\s*=\\s*@?\"(\\S*)\";");//匹配视图中的layout static InvalidatingCachingProvider cache = new InvalidatingCachingProvider(); static FileSystemWatcher m_Watcher = new FileSystemWatcher(); static CompileView() { var config = new TemplateServiceConfiguration(); config.BaseTemplateType = typeof(HuberImplementingTemplateBase<>); config.ReferenceResolver = new HuberReferenceResolver(); config.CachingProvider = cache; cache.InvalidateAll(); Engine.Razor = RazorEngineService.Create(config); //添加文件修改监控,以便在cshtml文件修改时重新编译该文件 m_Watcher.Path = HuberVariable.CurWebDir; m_Watcher.IncludeSubdirectories = true; m_Watcher.Filter = "*.*"; m_Watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName; m_Watcher.Created += new FileSystemEventHandler(OnChanged); m_Watcher.Changed += new FileSystemEventHandler(OnChanged); m_Watcher.Deleted += new FileSystemEventHandler(OnChanged); m_Watcher.EnableRaisingEvents = true; } //当视图被修改后清除缓存 private static void OnChanged(object sender, FileSystemEventArgs e) { if (e.FullPath.EndsWith(".cshtml")) { string s = e.FullPath.Replace(HuberVariable.CurWebDir, "/"); var key = Engine.Razor.GetKey(s); cache.InvalidateCache(key); } } public CompileView() { } public string RunCompile(ITemplateKey key, Type modelType, object model, DynamicViewBag viewBag) { //判断唯一视图的缓存 string path = (HuberVariable.CurWebDir + key.Name).Replace(@"\\", @"\"); ICompiledTemplate cacheTemplate; cache.TryRetrieveTemplate(key, null, out cacheTemplate); if (cacheTemplate == null || !cacheTemplate.Key.Name.Trim().Equals(key.Name.Trim())) { CompileViewAndLayout(key, null, model, viewBag); } //当缓存存在返回结果 return Engine.Razor.RunCompile(key, null, model, viewBag); } /// <summary> /// 编译视图和层layout /// </summary> /// <param name="key">视图的唯一路径</param> /// <param name="modelType">视图类型 :视图/layout</param> /// <param name="model">页面 MODEL</param> /// <param name="viewBag">viewBag</param> public void CompileViewAndLayout(ITemplateKey key, Type modelType, object model, DynamicViewBag viewBag) { //获取视图 string FullPath = (HuberVariable.CurWebDir + key.Name.Replace("/", @"\")).Replace(@"\\", @"\"); string content = System.IO.File.ReadAllText(FullPath); //匹配layout var matchs = layoutEx.Matches(content); string layoutPath = string.Empty; if (matchs != null) { foreach (Match m in matchs) { layoutPath = m.Groups[1].Value; } } if (layoutPath != string.Empty) { //添加layout到模板 string FullLayoutPath = (HuberVariable.CurWebDir + layoutPath.Replace("/", @"\")).Replace(@"\\", @"\"); if (File.Exists(FullLayoutPath)) { ITemplateKey layoutKey = Engine.Razor.GetKey(layoutPath, ResolveType.Layout); CompileViewAndLayout(layoutKey, null, model, viewBag); } } if (key.TemplateType == ResolveType.Layout) { Engine.Razor.AddTemplate(key, content); } else { //编译视图 Engine.Razor.RunCompile(content, key, null, model); } } }
InvalidatingCachingProvider是RazorEngine对视图文件编译结果的一种缓存策略,RazorEngine提供的缓存策略还有DefaultCachingProvider,也可以自己实现一种缓存策略只要继承ICachingProvider。
HuberImplementingTemplateBase:我们自定义的一种Razor模板标签,如“@Html.Raw”,这个例子也可以在RazorEngine官方文档中找到。我们还可以按照规则定义更多用法,下边是我的一些实现:
/// <summary>页面帮助类 /// A simple helper demonstrating the @Html.Raw /// </summary> /// <typeparam name="T"></typeparam> public class HuberImplementingTemplateBase<T> : TemplateBase<T> { /// <summary> /// A simple helper demonstrating the @Html.Raw /// </summary> public HuberImplementingTemplateBase() { Html = new RazorHtmlHelper(); } /// <summary> /// A simple helper demonstrating the @Html.Raw /// /// </summary> public RazorHtmlHelper Html { get; set; } } public class RazorHtmlHelper { /// <summary> /// 调用Action视图 /// </summary> /// <param name="actionName">action方法名称</param> /// <param name="controllerName">控制器名称</param> /// <returns></returns> public IEncodedString Action(string actionName, string controllerName) { return Action(actionName, controllerName, new { }); } /// <summary> /// 调用Action视图 /// </summary> /// <param name="actionName"></param> /// <param name="controllerName"></param> /// <param name="routeValues">传入参数</param> /// <returns></returns> public IEncodedString Action(string actionName, string controllerName, object routeValues) { RefRequestEntity paras = SetParamValue(routeValues); var t = HuberHttpModule.CurDomainAssembly.GetType(HuberHttpModule.CurDomainAssemblyName + ".Controllers." + controllerName + "Controller"); var m = t.GetMethod(actionName); object dObj = Activator.CreateInstance(t); object result = m.Invoke(dObj, new object[] { paras }); return new RawString((result as RefRespondEntity).ResultContext.ToString()); } /// <summary> /// 根据model设置传入参数 /// </summary> /// <param name="routeValues"></param> /// <returns></returns> private static RefRequestEntity SetParamValue(object routeValues) { RefRequestEntity paras = new RefRequestEntity(); Type t1 = routeValues.GetType(); PropertyInfo[] pis = t1.GetProperties(); foreach (PropertyInfo pi in pis) { paras.Request.Add(pi.Name, pi.GetValue(routeValues)); } return paras; } public IEncodedString RenderAction(string actionName, string controllerName) { return Action(actionName, controllerName, new { }); } public IEncodedString RenderAction(string actionName, string controllerName, object routeValues) { return Action(actionName, controllerName, routeValues); } public IEncodedString RenderPartial(string partialViewName, string controllerName) { return RenderPartial(partialViewName, controllerName, new { }, new DynamicViewBag()); } // Renders the partial view with the given view data and, implicitly, the given view data's model public IEncodedString RenderPartial(string partialViewName, string controllerName, DynamicViewBag ViewBag) { return RenderPartial(partialViewName, controllerName, new { }, ViewBag); } // Renders the partial view with an empty view data and the given model public IEncodedString RenderPartial(string partialViewName, string controllerName, object model) { return RenderPartial(partialViewName, controllerName, model, new DynamicViewBag()); } // Renders the partial view with a copy of the given view data plus the given model /// <summary> /// 部分视图 /// </summary> /// <param name="partialViewName">部分视图名称</param> /// <param name="controllerName">控制器名称</param> /// <param name="model"> model</param> /// <param name="ViewBag">ViewBag</param> /// <returns></returns> public IEncodedString RenderPartial(string partialViewName, string controllerName, object model, DynamicViewBag ViewBag) { RefRequestEntity paras = SetParamValue(model); var t = HuberHttpModule.CurDomainAssembly.GetType(HuberHttpModule.CurDomainAssemblyName + ".Controllers." + controllerName + "Controller"); var ActionFunc = t.GetMethod(partialViewName); object dObj = Activator.CreateInstance(t); var AddViewBageFunc = t.GetMethod("AddViewBageValues"); foreach (string key in ViewBag.GetDynamicMemberNames()) { AddViewBageFunc.Invoke(dObj, new object[] { key, Impromptu.InvokeGet(ViewBag, key) }); } object result = ActionFunc.Invoke(dObj, new object[] { paras }); return new RawString((result as RefRespondEntity).ResultContext.ToString()); } }
HuberReferenceResolver:我们定义的Razor中用的类库依赖。
public class HuberReferenceResolver : IReferenceResolver { static List<CompilerReference> compilerReference; static HuberReferenceResolver() { //加载本地所有类库,@using 使用 compilerReference = new List<CompilerReference>(); IEnumerable<string> loadedAssemblies = (new UseCurrentAssembliesReferenceResolver()) .GetReferences(null, null) .Select(r => r.GetFile()) .ToArray(); foreach (var l in loadedAssemblies) { compilerReference.Add(CompilerReference.From(l)); } } public string FindLoaded(IEnumerable<string> refs, string find) { return refs.First(r => r.EndsWith(System.IO.Path.DirectorySeparatorChar + find)); } public IEnumerable<CompilerReference> GetReferences(TypeContext context, IEnumerable<CompilerReference> includeAssemblies) { #region 加载依赖程序集 此处是加载所有程序集,效率需要改进 return compilerReference; #endregion } }
CompileViewAndLayout()是编译视图文件的主要部分,其中有路径的转换、key的定义规则等。
获取视图文件对应编译后的缓存key:Engine.Razor.GetKey();
编译模板文件(即layout部分):Engine.Razor.AddTemplate();
编译视图文件:Engine.Razor.RunCompile()。
转载请注明出处:http://www.cnblogs.com/eric-z/p/5102718.html