天天看點

第四篇 基于.net搭建熱插拔式web架構(RazorEngine實作)

  在開頭也是先給大家道個歉,由于最近準備婚事導緻這篇文章耽誤了許久,同時也謝謝老婆大人對我的支援。

   回顧上篇文章,我們重造了一個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