/// <inheritdoc /> public bool Equals(ViewLocationCacheKey y) { if (IsMainPage != y.IsMainPage || !string.Equals(ViewName, y.ViewName, StringComparison.Ordinal) || !string.Equals(ControllerName, y.ControllerName, StringComparison.Ordinal) || !string.Equals(AreaName, y.AreaName, StringComparison.Ordinal) || !string.Equals(PageName, y.PageName, StringComparison.Ordinal)) { return false; } if (ReferenceEquals(ViewLocationExpanderValues, y.ViewLocationExpanderValues)) { return true; } if (ViewLocationExpanderValues == null || y.ViewLocationExpanderValues == null || (ViewLocationExpanderValues.Count != y.ViewLocationExpanderValues.Count)) { return false; } foreach (var item in ViewLocationExpanderValues) { if (!y.ViewLocationExpanderValues.TryGetValue(item.Key, out var yValue) || !string.Equals(item.Value, yValue, StringComparison.Ordinal)) { return false; } } return true; }
从此处可以看出,如果 expanderValues 字典中 键/值对的数目不同或者其中任意一个值不同,那么这个 cacheKey 就是不同的。
我们继续往下分析, 从上文中我们知道,如果从ViewLookupCache 缓存中没有找到数据,那么它就会执行OnCacheMiss 方法。
我们找到OnCacheMiss 方法,如下所示:
private ViewLocationCacheResult OnCacheMiss( ViewLocationExpanderContext expanderContext, ViewLocationCacheKey cacheKey) { var viewLocations = GetViewLocationFormats(expanderContext); var expanders = _options.ViewLocationExpanders; // Read interface .Count once rather than per iteration var expandersCount = expanders.Count; for (var i = 0; i < expandersCount; i++) { viewLocations = expanders[i].ExpandViewLocations(expanderContext, viewLocations); } ViewLocationCacheResult cacheResult = null; var searchedLocations = new List<string>(); var expirationTokens = new HashSet<IChangeToken>(); foreach (var location in viewLocations) { var path = string.Format( CultureInfo.InvariantCulture, location, expanderContext.ViewName, expanderContext.ControllerName, expanderContext.AreaName); path = ViewEnginePath.ResolvePath(path); cacheResult = CreateCacheResult(expirationTokens, path, expanderContext.IsMainPage); if (cacheResult != null) { break; } searchedLocations.Add(path); } // No views were found at the specified location. Create a not found result. if (cacheResult == null) { cacheResult = new ViewLocationCacheResult(searchedLocations); } var cacheEntryOptions = new MemoryCacheEntryOptions(); cacheEntryOptions.SetSlidingExpiration(_cacheExpirationDuration); foreach (var expirationToken in expirationTokens) { cacheEntryOptions.AddExpirationToken(expirationToken); } return ViewLookupCache.Set(cacheKey, cacheResult, cacheEntryOptions); }
仔细观察之后你就会发现:
1、首先它是通过GetViewLocationFormats 方法获取初始的 viewLocations视图位置集合。
2、接着它会按顺序依次调用所有的ViewLocationExpander.ExpandViewLocations 方法,经过一系列聚合操作后得到最终的viewLocations 视图位置集合。
3、然后遍历 viewLocations 视图位置集合,按顺序依次去指定的路径中查找对应的视图,只要找到符合条件的第一个视图就结束循环,不再往下查找,最后设置缓存返回结果。
4、视图位置字符串(例如:“/Areas/{2}/WeChatViews/{1}/{0}.cshtml”)中的占位符含义:“{0}” 表示视图名称,“{1}” 表示控制器名称,“{2}” 表示区域名称。
下面我们继续找到GetViewLocationFormats 方法,如下所示: