BrnShopオープンソースネットショッピングモール第五講:カスタムビューエンジン

18639 ワード

今日のブログでは、カスタムビューエンジンについて説明します.aspではよく知られています.NetmvcフレームワークにはRazorビューエンジンがデフォルトで付属しています.また、IViewEngineインタフェースを実現するだけで、独自のビューエンジンをカスタマイズすることもできます.インタフェースの定義は以下の通りです.
  • ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
  • ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
  • void ReleaseView(ControllerContext controllerContext, IView view)

  • 次に、この3つの方法について詳しく説明します.まずFindViewメソッドです.このメソッドの役割は、指定されたコントローラコンテキストを使用して指定されたビューを検索することです.一般的な点は、コントローラでreturn View()などのメソッドを使用して指定されたビューファイルを検索するときに使用されることです.4つのパラメータは次のように解釈されます.
  • controllerContext:コントローラコンテキスト.これはみんなどういう意味か知っているから言わないでください.
  • viewName:ビュー名.コントローラでは、return View(ビュー名)
  • を一般的に指定します.
  • masterName:マスターページビュー名.つまり、ビューファイルで「Layout」で指定したビュー名
  • useCache:キャッシュを使用するかどうか.キャッシュを使用すると自動的にキャッシュレコードが検索され、見つかったらそのまま使用され、見つからない場合はディスク上で検索されます.キャッシュを使用しない場合は、
  • を毎回ディスクローカルで検索する必要があります.
    さらにFindPartialViewメソッド:このメソッドの役割は、指定されたコントローラコンテキストを使用して指定されたパーティションビューを検索することであり、上記のメソッドと比較して、このメソッドがパーティションビューを検索するために使用されていることがわかります.コントローラで「return PartialView(」セクションビュー名")などのメソッドを使用して、指定したセクションビューファイルを検索するときに使用します.その3つのパラメータ(サブビューにマスターページがないためmasterNameパラメータは必要ありません)は、次のように説明されています.
  • controllerContext:コントローラコンテキスト.
  • partialViewName:セクションビュー名.コントローラでは、return PartialView(「セクションビュー名」)
  • を一般的に指定します.
  • useCache:キャッシュを使用するかどうか.詳細については、FindViewメソッド
  • を参照してください.
    最後にReleaseViewメソッドです.このメソッドの役割は、指定したコントローラコンテキストを使用して指定したビューを解放することです.つまり、ビューで使用されているリソースを解放することです.そのパラメータは比較的簡単で、具体的には以下の通りです.
  • controllerContext:コントローラコンテキスト.
  • view:ビュー.すべてのビューファイルが実行時にクラスにコンパイルされることを知っています.このクラスはIViewインタフェースを実現しているので、このパラメータはこのクラスを指します.

  •   
    独自のビューエンジンをカスタマイズするには、クラスをカスタマイズし、IViewEngineインタフェースを継承して3つの方法を実装するだけでいいです.しかし、このようにすると私たちの仕事量が大きくなるので、もっと便利な方法はありませんか?答えはあります.それは、VirtualPathProviderViewEngineクラスを継承し、FindViewとFindPartialViewメソッドを書き換えることです(ReleaseViewメソッドは書き換えず、VirtualPathProviderViewEngineの実装を直接使用できます).次に、VirtualPathProviderViewEngineがどのようなものなのかを見てみましょう.コードは次のとおりです.
    abstract class VirtualPathProviderViewEngine : IViewEngine
    

    VirtualPathProviderViewEngineクラスのコードが多すぎるため、その定義だけを投稿しました.VirtualPathProviderViewEngineクラスがIViewEngineインタフェースを実現していることがわかります.このクラスを使用して、カスタムビューエンジンの複雑さとワークロードを簡素化できます.
     
    次に、マルチショップ版のオンラインショッピングモールBrnMallを例に挙げてみましょう.
    まずクラスを定義し、次のコードでVirtualPathProviderViewEngineから継承します.
        /// <summary>
        ///            
        /// </summary>
        public abstract class ThemeVirtualPathProviderViewEngine : VirtualPathProviderViewEngine
    

    このクラスを継承すると、FindViewメソッドとFindPartialViewメソッドを書き換えることができます.コードは次のとおりです.
            #region     
    
            /// <summary>
            ///                           
            /// </summary>
            /// <param name="controllerContext">      </param>
            /// <param name="viewName">     </param>
            /// <param name="masterName">       </param>
            /// <param name="useCache">   true,        </param>
            /// <returns>   </returns>
            public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
            {
                //         
                bool mobile = WebHelper.IsMobile();
    
                //       ,           
                string overrideViewName = mobile ? string.Format("{0}.{1}", viewName, _mobileviewmodifier) : viewName;
                //          
                ViewEngineResult result = FindThemeView(controllerContext, overrideViewName, masterName, useCache, mobile);
    
                //                          
                if (mobile && (result == null || result.View == null))
                    result = FindThemeView(controllerContext, viewName, masterName, useCache, false);
                return result;
    
            }
    
            /// <summary>
            ///          
            /// </summary>
            /// <param name="controllerContext">      </param>
            /// <param name="partialViewName">       </param>
            /// <param name="useCache">   true,          </param>
            /// <returns>    </returns>
            public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
            {
                //         
                bool mobile = WebHelper.IsMobile();
    
                //       ,             
                string overrideViewName = mobile ? string.Format("{0}.{1}", partialViewName, _mobileviewmodifier) : partialViewName;
                //            
                ViewEngineResult result = FindThemePartialView(controllerContext, overrideViewName, useCache, mobile);
    
                //                              
                if (mobile && (result == null || result.View == null))
                    result = FindThemePartialView(controllerContext, partialViewName, useCache, false);
                return result;
            }
    
            #endregion
    

    この2つの方法の中で私たちは自分の必要に応じて必要なものを追加することができます.たとえば、BrnMallショッピングモールでは、訪問者が携帯電話ブラウザを使用してショッピングモールにアクセスするかどうかを判断する必要があります.そうであれば、携帯電話ブラウザの最適化に特化したビューに切り替えます(すなわち、ビュー名の後ろに.Mobileを追加し、新しいビュー名を構築します).具体的なコード実装については、上記を参照してください.
    今、私たちのビジネスはビューエンジンに融合する必要があります.次に、ディスクに行って対応するビューファイルを検索します.しかし、検索する前に、ディスク検索パスの結果を構築する必要があります.それは、ビューファイルが見つからないときにヒントを与えるのに便利です.そのため、上記の2つの書き換え方法では、すぐにディスクに行ってビューファイルを検索しません.検索パスの結果を構築する2つの方法が呼び出されます.具体的には、次のとおりです.
            /// <summary>
            ///         
            /// </summary>
            private ViewEngineResult FindThemeView(ControllerContext controllerContext, string viewName, string masterName, bool useCache, bool mobile)
            {
                //          
                string[] strArray1 = null;
                //          
                string[] strArray2 = null;
    
                //       
                string controllerName = controllerContext.RouteData.GetRequiredString("controller");
    
                //        
                string viewPath = GetPath(controllerContext, viewName, controllerName, "View", useCache, mobile, out strArray1);
    
                //        
                if (!string.IsNullOrWhiteSpace(viewPath))
                {
                    if (string.IsNullOrWhiteSpace(masterName))
                    {
                        return new ViewEngineResult(CreateView(controllerContext, viewPath, string.Empty), this);
                    }
                    else
                    {
                        //         
                        string masterPath = GetPath(controllerContext, masterName, controllerName, "Master", useCache, mobile, out strArray2);
                        if (!string.IsNullOrWhiteSpace(masterPath))
                        {
                            return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this);
                        }
                    }
                }
    
                //                     
                if (strArray2 == null)
                {
                    return new ViewEngineResult(strArray1);
                }
                else
                {
                    return new ViewEngineResult(strArray1.Union<string>(strArray2));
                }
            }
    
            /// <summary>
            ///           
            /// </summary>
            private ViewEngineResult FindThemePartialView(ControllerContext controllerContext, string partialViewName, bool useCache, bool mobile)
            {
                //            
                string[] strArray;
    
                //       
                string controllerName = controllerContext.RouteData.GetRequiredString("controller");
    
                //          
                string partialViewPath = GetPath(controllerContext, partialViewName, controllerName, "Partial", useCache, mobile, out strArray);
    
                //          
                if (!string.IsNullOrWhiteSpace(partialViewPath))
                {
                    return new ViewEngineResult(CreatePartialView(controllerContext, partialViewPath), this);
                }
                //                
                return new ViewEngineResult(strArray);
            }
    

    上記の方法では、GetPath方法によって返された結果に基づいて、以下のように処理します.
  • 返された結果が空の場合は、ビューファイルが存在しないことを意味します.ビューファイル検索パスを配列に構築し、ViewEngineResultクラスのpublic ViewEngineResult(IEnumerablesearchedLocations)コンストラクション関数
  • を呼び出す必要があります.
  • 返された結果が空ではなく、ビューファイルが存在することを示す場合、ViewEngineResultクラスのpublic ViewEngineResult(IView view,IViewEngine viewEngine)コンストラクション関数を呼び出します.

  • 上の2点について補足する必要があります.
  • ViewEngineResultクラス:このクラスはビューエンジンの検索結果を表し、IEnumerableタイプのSearchedLocationsとIViewタイプのViewの2つのプロパティがあります.コンストラクション関数ViewEngineResult(IEnumerablesearchedLocations)を使用して初期化すると、パラメータsearchedLocationsが属性SearchedLocationsに割り当てられ、属性Viewが空になります.コンストラクション関数ViewEngineResult(IView view,IViewEngine viewEngine)を使用して初期化すると、パラメータviewが属性Viewに割り当てられ、属性SearchedLocationsが空になります.MVCフレームワークは、属性Viewが空であるかどうかによって異なる結果を表示し、View属性が空である場合は直接ビューパス検索結果を出力し、空でない場合はこのビューを出力する.
  • CreateViewメソッドとCreatePartialViewメソッド:ビューファイルパスを対応するビュー(IViewインタフェースを実装するクラス)
  • に変換できます.
    すべてそろっていて、今ビューファイルの経路を探すことができて、私达は多店版のネットショッピングモールBrnMallを例にして、具体的なコードは以下の通りです:
            /// <summary>
            ///        
            /// </summary>
            private string GetPath(ControllerContext controllerContext, string name, string controllerName, string cacheKeyPrefix, bool useCache, bool mobile, out string[] searchedLocations)
            {
                searchedLocations = null;
    
                //      
                string[] locations = null;
                //  
                string theme = string.Empty;
    
                //    
                string area = GetRouteDataTokenValue("area", controllerContext.RouteData.DataTokens).ToLower();
                if (string.IsNullOrWhiteSpace(area))//           
                {
                    theme = GetRouteDataTokenValue("theme", controllerContext.RouteData.DataTokens);
                    if (theme == "")//    
                    {
                        locations = new string[2] { "~/Views/{1}/{0}.cshtml", "~/Views/Shared/{0}.cshtml" };
                    }
                    else//    
                    {
                        locations = new string[1] { "~/Themes/{2}/Views/{0}.cshtml" };
                    }
                }
                else//                
                {
                    //          
                    if (mobile)
                    {
                        searchedLocations = new string[0];
                        return string.Empty;
                    }
                    if (area == "storeadmin")//          
                    {
                        locations = new string[2] { "~/Admin_Store/Views/{1}/{0}.cshtml", "~/Admin_Store/Views/Shared/{0}.cshtml" };
                    }
                    else if (area == "malladmin")//          
                    {
                        locations = new string[2] { "~/Admin_Mall/Views/{1}/{0}.cshtml", "~/Admin_Mall/Views/Shared/{0}.cshtml" };
                    }
                }
    
                //          
                bool flag2 = IsSpecificName(name);
    
                //          
                string cacheKey = CreateCacheKey(cacheKeyPrefix, name, flag2 ? string.Empty : controllerName, area, theme);//        
                if (useCache)
                {
                    //          
                    var cachedPath = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, cacheKey);
                    if (cachedPath != null)
                    {
                        return cachedPath;
                    }
                }
    
                //           ,              
                if (!flag2)//          
                {
                    return GetPathFromGeneralName(controllerContext, locations, name, controllerName, theme, cacheKey, ref searchedLocations);
                }
                else//        
                {
                    return GetPathFromSpecificName(controllerContext, name, cacheKey, ref searchedLocations);
                }
            }
    

    この方法では,現在アクセスしている場所(ショッピングモールページ,店舗ページ,店舗バックグラウンドページ,システムバックグラウンドページ)に基づいて異なる検索経路を構築し,具体的には商品のif else文を参照することができる.
    ここで2つの点に注意してください.1つ目は、ビューファイル名のフォーマットの問題です.
    コントローラのViewメソッドでは、ビューファイルの名前を入力したり、ビューファイルのパスを入力したりすることができます.コードは次のとおりです.
    return View("      ");//         
    return View("~/Views/      .cshtml");//         
    

    したがって、このときのビュー名のタイプを判断する必要があります.具体的なコードは次のとおりです.
            /// <summary>
            ///          “~” “/”  
            /// </summary>
            private bool IsSpecificName(string name)
            {
                char ch = name[0];
                if (ch != '~')
                {
                    return (ch == '/');
                }
                return true;
            }
    

    2つ目はビューパスキャッシュの問題です.パラメータuseCacheが真の場合、まずキャッシュでパスを取得し、キャッシュに存在する場合は直接戻ります.そうしないと、ディスク上で検索します.キャッシュキーの生成コードは次のとおりです.
            /// <summary>
            ///           
            /// </summary>
            private string CreateCacheKey(string prefix, string name, string controllerName, string area, string theme)
            {
                return string.Format(":ViewCacheKey:{0}:{1}:{2}:{3}:{4}:{5}", new object[] { base.GetType().AssemblyQualifiedName, prefix, name, controllerName, area, theme });
            }
    

    最後に、本物のビューファイルパスが見つかりました.具体的なコードは次のとおりです.
            /// <summary>
            ///            
            /// </summary>
            private string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey, ref string[] searchedLocations)
            {
                //            
                ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, name);
                //     “.cshtml”        
                if (!IsSupportedExtension(name) || !FileExists(controllerContext, name))
                {
                    searchedLocations = new string[] { name };
                    return string.Empty;
                }
                return name;
            }
    
            /// <summary>
            ///            
            /// </summary>
            private string GetPathFromGeneralName(ControllerContext controllerContext, string[] viewLocationFormats, string name, string controllerName, string theme, string cacheKey, ref string[] searchedLocations)
            {
                int count = viewLocationFormats.Length;
                searchedLocations = new string[count];
    
                //      
                for (int i = 0; i < count; i++)
                {
                    string path = string.Format(viewLocationFormats[i], name, controllerName, theme);
                    if (FileExists(controllerContext, path))
                    {
                        searchedLocations = null;
                        ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, path);
                        return path;
                    }
                    //             
                    searchedLocations[i] = path;
                }
    
                return string.Empty;
            }
    

    ビュー名がビューパスフォーマットの場合、パスで指定したファイルが存在するかどうかを直接検索します.ビュー名が通常の名前の場合、パスフォーマットリストに基づいてコンポーネントの実際のパスを順に検索します.いずれの場合も、ビューファイルが存在しない場合は検索パスを出力し、ビューファイルが存在し、キャッシュが使用されている場合は、次回直接使用するためにキャッシュに保存します.
    パス検索クラスをカスタマイズしただけなので、完全なビューエンジンを構築するには2つのステップが必要です.最初のステップは、次のようなコードでトピック構築エンジンを実現します.
        /// <summary>
        ///       
        /// </summary>
        public abstract class ThemeBuildManagerViewEngine : ThemeVirtualPathProviderViewEngine
        {
            //        
            protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
            {
                return BuildManager.GetObjectFactory(virtualPath, false) != null;
            }
        }
    

    2つ目のステップは、ビューエンジンを実装することです.コードは次のとおりです.
        /// <summary>
        ///       
        /// </summary>
        public class ThemeRazorViewEngine : ThemeBuildManagerViewEngine
        {
            /// <summary>
            ///   Razor  
            /// </summary>
            protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
            {
                return new RazorView(controllerContext, viewPath, masterPath, true, FileExtensions);
            }
    
            /// <summary>
            ///   Razor    
            /// </summary>
            protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
            {
                return new RazorView(controllerContext, partialPath, null, false, FileExtensions);
            }
        }
    

    これでカスタムビューエンジンは完了しました.このエンジンを使用するにはGlobalだけです.asaxでデフォルトのビューエンジンを置き換えればいいです.コードは次のとおりです.
            protected void Application_Start()
            {
                //          ThemeRazorViewEngine  
                ViewEngines.Engines.Clear();
                ViewEngines.Engines.Add(new ThemeRazorViewEngine());
    
            }
    

      
    ネットショッピングモールのプログラムの设计に兴味のある友达がいて、QQ群に参加することを歓迎します:235274151、みんなは交流することができます!