/// <summary>
        /// Gets an ordered <see cref="IReadOnlyList{ChunkTreeResult}"/> of parsed <see cref="ChunkTree"/>s and
        /// file paths for each <c>_ViewImports</c> that is applicable to the page located at
        /// <paramref name="pagePath"/>. The list is ordered so that the <see cref="ChunkTreeResult"/>'s
        /// <see cref="ChunkTreeResult.ChunkTree"/> for the <c>_ViewImports</c> closest to the
        /// <paramref name="pagePath"/> in the file system appears first.
        /// </summary>
        /// <param name="pagePath">The path of the page to locate inherited chunks for.</param>
        /// <returns>A <see cref="IReadOnlyList{ChunkTreeResult}"/> of parsed <c>_ViewImports</c>
        /// <see cref="ChunkTree"/>s and their file paths.</returns>
        /// <remarks>
        /// The resulting <see cref="IReadOnlyList{ChunkTreeResult}"/> is ordered so that the result
        /// for a _ViewImport closest to the application root appears first and the _ViewImport
        /// closest to the page appears last i.e.
        /// [ /_ViewImport, /Views/_ViewImport, /Views/Home/_ViewImport ]
        /// </remarks>
        public virtual IReadOnlyList <ChunkTreeResult> GetInheritedChunkTreeResults(string pagePath)
        {
            if (pagePath == null)
            {
                throw new ArgumentNullException(nameof(pagePath));
            }

            var inheritedChunkTreeResults = new List <ChunkTreeResult>();
            var templateEngine            = new RazorTemplateEngine(_razorHost);

            foreach (var viewImportsPath in ViewHierarchyUtility.GetViewImportsLocations(pagePath))
            {
                // viewImportsPath contains the app-relative path of the _ViewImports.
                // Since the parsing of a _ViewImports would cause parent _ViewImports to be parsed
                // we need to ensure the paths are app-relative to allow the GetGlobalFileLocations
                // for the current _ViewImports to succeed.
                var chunkTree = _chunkTreeCache.GetOrAdd(
                    viewImportsPath,
                    fileInfo => ParseViewFile(
                        templateEngine,
                        fileInfo,
                        viewImportsPath));

                if (chunkTree != null)
                {
                    var result = new ChunkTreeResult(chunkTree, viewImportsPath);
                    inheritedChunkTreeResults.Insert(0, result);
                }
            }

            return(inheritedChunkTreeResults);
        }
Esempio n. 2
0
        /// <summary>
        /// Gets an ordered <see cref="IReadOnlyList{T}"/> of parsed <see cref="ChunkTree"/> for each
        /// <c>_ViewImports</c> that is applicable to the page located at <paramref name="pagePath"/>. The list is
        /// ordered so that the <see cref="ChunkTree"/> for the <c>_ViewImports</c> closest to the
        /// <paramref name="pagePath"/> in the file system appears first.
        /// </summary>
        /// <param name="pagePath">The path of the page to locate inherited chunks for.</param>
        /// <returns>A <see cref="IReadOnlyList{ChunkTree}"/> of parsed <c>_ViewImports</c>
        /// <see cref="ChunkTree"/>s.</returns>
        public virtual IReadOnlyList <ChunkTree> GetInheritedChunkTrees([NotNull] string pagePath)
        {
            var inheritedChunkTrees = new List <ChunkTree>();
            var templateEngine      = new RazorTemplateEngine(_razorHost);

            foreach (var viewImportsPath in ViewHierarchyUtility.GetViewImportsLocations(pagePath))
            {
                // viewImportsPath contains the app-relative path of the _ViewImports.
                // Since the parsing of a _ViewImports would cause parent _ViewImports to be parsed
                // we need to ensure the paths are app-relative to allow the GetGlobalFileLocations
                // for the current _ViewImports to succeed.
                var chunkTree = _chunkTreeCache.GetOrAdd(
                    viewImportsPath,
                    fileInfo => ParseViewFile(
                        templateEngine,
                        fileInfo,
                        viewImportsPath));

                if (chunkTree != null)
                {
                    inheritedChunkTrees.Add(chunkTree);
                }
            }

            return(inheritedChunkTrees);
        }
Esempio n. 3
0
        private MemoryCacheEntryOptions GetMemoryCacheEntryOptions(
            RelativeFileInfo fileInfo,
            PrecompilationCacheEntry cacheEntry)
        {
            var options = new MemoryCacheEntryOptions();

            options.AddExpirationToken(FileProvider.Watch(fileInfo.RelativePath));
            foreach (var path in ViewHierarchyUtility.GetViewImportsLocations(fileInfo.RelativePath))
            {
                options.AddExpirationToken(FileProvider.Watch(path));
            }
            return(options);
        }
Esempio n. 4
0
        private MemoryCacheEntryOptions GetMemoryCacheEntryOptions(string relativePath)
        {
            var options = new MemoryCacheEntryOptions();

            options.AddExpirationTrigger(_fileProvider.Watch(relativePath));

            var viewImportsPaths = ViewHierarchyUtility.GetViewImportsLocations(relativePath);

            foreach (var location in viewImportsPaths)
            {
                options.AddExpirationTrigger(_fileProvider.Watch(location));
            }
            return(options);
        }
Esempio n. 5
0
        internal CompilerCache(IEnumerable <RazorFileInfoCollection> razorFileInfoCollections,
                               IAssemblyLoadContext loadContext,
                               IFileProvider fileProvider)
        {
            _fileProvider = fileProvider;
            _cache        = new MemoryCache(new MemoryCacheOptions {
                CompactOnMemoryPressure = false
            });

            var cacheEntries = new List <CompilerCacheEntry>();

            foreach (var viewCollection in razorFileInfoCollections)
            {
                var containingAssembly = viewCollection.LoadAssembly(loadContext);
                foreach (var fileInfo in viewCollection.FileInfos)
                {
                    var viewType   = containingAssembly.GetType(fileInfo.FullTypeName);
                    var cacheEntry = new CompilerCacheEntry(fileInfo, viewType);

                    // There shouldn't be any duplicates and if there are any the first will win.
                    // If the result doesn't match the one on disk its going to recompile anyways.
                    var normalizedPath = NormalizePath(fileInfo.RelativePath);
                    _cache.Set(
                        normalizedPath,
                        cacheEntry,
                        GetMemoryCacheEntryOptions(fileInfo.RelativePath));

                    cacheEntries.Add(cacheEntry);
                }
            }

            // Set up _ViewImports
            foreach (var entry in cacheEntries)
            {
                var globalFileLocations = ViewHierarchyUtility.GetViewImportsLocations(entry.RelativePath);
                foreach (var location in globalFileLocations)
                {
                    var globalFileEntry = _cache.Get <CompilerCacheEntry>(location);
                    if (globalFileEntry != null)
                    {
                        // Add the composite _ViewImports entry as a dependency.
                        entry.AssociatedGlobalFileEntry = globalFileEntry;
                        break;
                    }
                }
            }
        }
Esempio n. 6
0
        // Returns the entry for the nearest _ViewImports that the file inherits directives from. Since _ViewImports
        // entries are affected by other _ViewImports entries that are in the path hierarchy, the returned value
        // represents the composite result of performing a cache check on individual _ViewImports entries.
        private CompilerCacheEntry GetCompositeGlobalFileEntry(string relativePath,
                                                               Func <RelativeFileInfo, CompilationResult> compile)
        {
            var viewImportsLocations = ViewHierarchyUtility.GetViewImportsLocations(relativePath);

            foreach (var viewImports in viewImportsLocations)
            {
                var getOrAddResult = GetOrAddCore(viewImports, compile);
                if (getOrAddResult != null)
                {
                    // This is the nearest _ViewImports that exists on disk.
                    return(getOrAddResult.CompilerCacheEntry);
                }
            }

            // No _ViewImports discovered.
            return(null);
        }