zoukankan      html  css  js  c++  java
  • asp.net mvc源码分析ActionResult篇 FindView

    接着上篇asp.net mvc源码分析-ActionResult篇 ViewResult 中有ViewEngineResult result = ViewEngineCollection.FindView(context, ViewName, MasterName)这么一句,它究竟是怎么找到View的了?首先放我们看看你ViewEngineCollection中的FindView方法吧,其实就一句

    return Find(e => e.FindView(controllerContext, viewName, masterName, true),
                            e => e.FindView(controllerContext, viewName, masterName, false));

    不过这句干的事情可不少啊,调用内部的一个Find方法,

        private ViewEngineResult Find(Func<IViewEngine, ViewEngineResult> cacheLocator, Func<IViewEngine, ViewEngineResult> locator) {
                // First, look up using the cacheLocator and do not track the searched paths in non-matching view engines
                // Then, look up using the normal locator and track the searched paths so that an error view engine can be returned
                return Find(cacheLocator, trackSearchedPaths: false)
                    ?? Find(locator, trackSearchedPaths: true);

            }

    这里的cacheLocator=e.FindView(controllerContext, viewName, masterName, true),locator=e.FindView(controllerContext, viewName, masterName, false),它也是在调用一个内部的find方法,

       private ViewEngineResult Find(Func<IViewEngine, ViewEngineResult> lookup, bool trackSearchedPaths) {
                // Returns
                //    1st result
                // OR list of searched paths (if trackSearchedPaths == true)
                // OR null
                ViewEngineResult result;
    
                List<string> searched = null;
                if (trackSearchedPaths) {
                    searched = new List<string>();
                }
    
                foreach (IViewEngine engine in CombinedItems) {
                    if (engine != null) {
                        result = lookup(engine);
    
                        if (result.View != null) {
                            return result;
                        }
    
                        if (trackSearchedPaths) {
                            searched.AddRange(result.SearchedLocations);
                        }
                    }
                }
    
                if (trackSearchedPaths) {
                    // Remove duplicate search paths since multiple view engines could have potentially looked at the same path
                    return new ViewEngineResult(searched.Distinct().ToList());
                }
                else {
                    return null;
                }
            }
    

     其中 trackSearchedPaths表示是否记录收索路径,首先不记录收索路径看能否找到view,如果找不到就记录收索路径在此查找view。顺便提一下ViewEngineCollection中有一个FindPartialView方法和FindView方法逻辑一致

    这里 的CombinedItems其实就是ViewEngines中的Engines属性,默认只有WebFormViewEngine、RazorViewEngine这2个实例。所以它会遍历所有的IViewEngine来查找view。这里为了提高性能,我们可以移除一个我们不需要的IViewEngine。,例如我在用Razor开发mvc时可以把WebFormViewEngine给移除以提高性。Application_Start方法中添加  ViewEngines.Engines.RemoveAt(0);

    trackSearchedPaths这个参数是记录查找路径的,究竟有什么效果了?如果我们在IViewEngine中没有找到相应的view则记录查找路径。 

       if (trackSearchedPaths) {
                            searched.AddRange(result.SearchedLocations);
                        }

    如果 所有的IViewEngine都找完了,都没有找到查找到view,trackSearchedPaths为false值直接返回null,否者返回一个没有view的ViewEngineResult。

    给我们带来效果如下:

    现在 我们来看看你真正查找view的方法是在VirtualPathProviderViewEngine类的FindView方法:

            public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) {
                if (controllerContext == null) {
                    throw new ArgumentNullException("controllerContext");
                }
                if (String.IsNullOrEmpty(viewName)) {
                    throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewName");
                }
    
                string[] viewLocationsSearched;
                string[] masterLocationsSearched;
    
                string controllerName = controllerContext.RouteData.GetRequiredString("controller");
                string viewPath = GetPath(controllerContext, ViewLocationFormats, AreaViewLocationFormats, "ViewLocationFormats", viewName, controllerName, _cacheKeyPrefix_View, useCache, out viewLocationsSearched);
                string masterPath = GetPath(controllerContext, MasterLocationFormats, AreaMasterLocationFormats, "MasterLocationFormats", masterName, controllerName, _cacheKeyPrefix_Master, useCache, out masterLocationsSearched);
    
                if (String.IsNullOrEmpty(viewPath) || (String.IsNullOrEmpty(masterPath) && !String.IsNullOrEmpty(masterName))) {
                    return new ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched));
                }
    
                return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this);
            }
    

     这里面有一句很重要  string viewPath = GetPath(controllerContext, ViewLocationFormats, AreaViewLocationFormats, "ViewLocationFormats", viewName, controllerName, _cacheKeyPrefix_View, useCache, out viewLocationsSearched);,用GetPath来找viewpath、masterPath。

     private string GetPath(ControllerContext controllerContext, string[] locations, string[] areaLocations, string locationsPropertyName, string name, string controllerName, string cacheKeyPrefix, bool useCache, out string[] searchedLocations) {
                searchedLocations = _emptyLocations;
    
                if (String.IsNullOrEmpty(name)) {
                    return String.Empty;
                }
    
                string areaName = AreaHelpers.GetAreaName(controllerContext.RouteData);
                bool usingAreas = !String.IsNullOrEmpty(areaName);
                List<ViewLocation> viewLocations = GetViewLocations(locations, (usingAreas) ? areaLocations : null);
    
                if (viewLocations.Count == 0) {
                    throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
                        MvcResources.Common_PropertyCannotBeNullOrEmpty, locationsPropertyName));
                }
    
                bool nameRepresentsPath = IsSpecificPath(name);
                string cacheKey = CreateCacheKey(cacheKeyPrefix, name, (nameRepresentsPath) ? String.Empty : controllerName, areaName);
    
                if (useCache) {
                    return ViewLocationCache.GetViewLocation(controllerContext.HttpContext, cacheKey);
                }
    
                return (nameRepresentsPath) ?
                    GetPathFromSpecificName(controllerContext, name, cacheKey, ref searchedLocations) :
                    GetPathFromGeneralName(controllerContext, viewLocations, name, controllerName, areaName, cacheKey, ref searchedLocations);
            }
    

      

     string areaName = AreaHelpers.GetAreaName(controllerContext.RouteData);
                bool usingAreas = !String.IsNullOrEmpty(areaName);
    默认情况下usingAreas 为false。GetViewLocations方法就是返回一个ViewLocation集合,里面的代码简单,举个例子吧,在RazorViewEngine中重置了

      ViewLocationFormats = new[] {
                    "~/Views/{1}/{0}.cshtml",
                    "~/Views/{1}/{0}.vbhtml",
                    "~/Views/Shared/{0}.cshtml",
                    "~/Views/Shared/{0}.vbhtml"
                };

    加入当前的Controller为Home,Action为Index,

    那么 后面实际 查找路径就会一次是

    "~/Views/Home/Index.cshtml",
    "~/Views/Home/Index.vbhtml",
    "~/Views/Shared/Index.cshtml",
    "~/Views/Shared/Index.vbhtml"

    其实我们一般的项目要么就是C#和VB一种语言开发,所以ViewLocationFormats 可以移除2个元素以提高性能

     bool nameRepresentsPath = IsSpecificPath(name)这句是看我们的viewname是否以~和/开头.

       string cacheKey = CreateCacheKey(cacheKeyPrefix, name, (nameRepresentsPath) ? String.Empty : controllerName, areaName); 创建一个缓存key。

     if (useCache) {
                    return ViewLocationCache.GetViewLocation(controllerContext.HttpContext, cacheKey);
                }

    从 缓存中返回viewpath, 默认情况相爱ViewLocationCache = new DefaultViewLocationCache()

    DefaultViewLocationCache的主要方法如下:

     public string GetViewLocation(HttpContextBase httpContext, string key) {
                if (httpContext == null) {
                    throw new ArgumentNullException("httpContext");
                }
                return (string)httpContext.Cache[key];
            }
            public void InsertViewLocation(HttpContextBase httpContext, string key, string virtualPath) {
                if (httpContext == null) {
                    throw new ArgumentNullException("httpContext");
                }
                httpContext.Cache.Insert(key, virtualPath, null /* dependencies */, Cache.NoAbsoluteExpiration, TimeSpan);
            }

    很简单的。

    现在 我们回到GetPath方法中,如果不用缓存就 只剩下最后一句了

     return (nameRepresentsPath) ?
                    GetPathFromSpecificName(controllerContext, name, cacheKey, ref searchedLocations) :
                    GetPathFromGeneralName(controllerContext, viewLocations, name, controllerName, areaName, cacheKey, ref searchedLocations);

    这 2个方法实现如下:

        private string GetPathFromGeneralName(ControllerContext controllerContext, List<ViewLocation> locations, string name, string controllerName, string areaName, string cacheKey, ref string[] searchedLocations) {
                string result = String.Empty;
                searchedLocations = new string[locations.Count];
    
                for (int i = 0; i < locations.Count; i++) {
                    ViewLocation location = locations[i];
                    string virtualPath = location.Format(name, controllerName, areaName);
    
                    if (FileExists(controllerContext, virtualPath)) {
                        searchedLocations = _emptyLocations;
                        result = virtualPath;
                        ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, result);
                        break;
                    }
    
                    searchedLocations[i] = virtualPath;
                }
    
                return result;
            }
    
            private string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey, ref string[] searchedLocations) {
                string result = name;
    
                if (!(FilePathIsSupported(name) && FileExists(controllerContext, name))) {
                    result = String.Empty;
                    searchedLocations = new[] { name };
                }
    
                ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, result);
                return result;
            }
    

      这2个方法都调用一个共同的方法FileExists,FileExists是个内联函数直接调用VirtualPathProvider.FileExists(virtualPath),默认情况下VirtualPathProvider=HostingEnvironment.VirtualPathProvider。但是实际上调用的是 BuildManager.GetObjectFactory(virtualPath, false) != null它是如何检查路径的我们这里就忽略它吧。在GetPathFromSpecificName方法中调用一个FilePathIsSupported方法,其实就是检查viewname的扩展名。
    这里我们就完成可viewpath的查找。

    现在 我们回到FindView中来,

      if (String.IsNullOrEmpty(viewPath) || (String.IsNullOrEmpty(masterPath) && !String.IsNullOrEmpty(masterName))) {
                    return new ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched));
                }

    这句 没什么说的了简单, return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this);这句返回一个ViewEngineResult,ViewEngineResult的构造函数也很简单,多说了。现在我们来看看这个CreateView方法。RazorViewEngine和WebFormViewEngine的具体实现如下:

     protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) {
                var view = new RazorView(controllerContext, viewPath,
                                         layoutPath: masterPath, runViewStartPages: true, viewStartFileExtensions: FileExtensions, viewPageActivator: ViewPageActivator);
                return view;
            }
      protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) {
                return new WebFormView(controllerContext, viewPath, masterPath, ViewPageActivator);
            }

    至此 我们已经成功找到了View。

     

  • 相关阅读:
    201771010135 杨蓉庆《面对对象程序设计(java)》第十五周学习总结
    201771010135 杨蓉庆/张燕/杨玲《面对对象程序设计(java)》第十四周学习总结
    201771010135 杨蓉庆/张燕《面对对象程序设计(java)》第十三周学习总结
    团队作业6—复审与事后分析
    团队作业6——Alpha阶段项目复审
    团队作业6——事后分析
    团队作业5——测试与发布(Alpha版本)
    团队作业4-项目汇总
    团队作业4-Day7
    团队作业4-Day6
  • 原文地址:https://www.cnblogs.com/majiang/p/2764976.html
Copyright © 2011-2022 走看看