public PageActionDescriptorChangeProvider( RazorTemplateEngine templateEngine, IRazorViewEngineFileProviderAccessor fileProviderAccessor, IOptions <RazorPagesOptions> razorPagesOptions) { if (templateEngine == null) { throw new ArgumentNullException(nameof(templateEngine)); } if (fileProviderAccessor == null) { throw new ArgumentNullException(nameof(fileProviderAccessor)); } if (razorPagesOptions == null) { throw new ArgumentNullException(nameof(razorPagesOptions)); } _fileProvider = fileProviderAccessor.FileProvider; var rootDirectory = razorPagesOptions.Value.RootDirectory; Debug.Assert(!string.IsNullOrEmpty(rootDirectory)); rootDirectory = rootDirectory.TrimEnd('/'); var importFileAtPagesRoot = rootDirectory + "/" + templateEngine.Options.ImportsFileName; _additionalFilesToTrack = templateEngine.GetImportItems(importFileAtPagesRoot) .Select(item => item.FilePath) .ToArray(); _searchPattern = rootDirectory + "/**/*.cshtml"; }
public PageActionDescriptorChangeProvider( RazorTemplateEngine templateEngine, IRazorViewEngineFileProviderAccessor fileProviderAccessor, IOptions <RazorPagesOptions> razorPagesOptions) { if (templateEngine == null) { throw new ArgumentNullException(nameof(templateEngine)); } if (fileProviderAccessor == null) { throw new ArgumentNullException(nameof(fileProviderAccessor)); } if (razorPagesOptions == null) { throw new ArgumentNullException(nameof(razorPagesOptions)); } _fileProvider = fileProviderAccessor.FileProvider; var rootDirectory = razorPagesOptions.Value.RootDirectory; Debug.Assert(!string.IsNullOrEmpty(rootDirectory)); rootDirectory = rootDirectory.TrimEnd('/'); // Search pattern that matches all cshtml files under the Pages RootDirectory var pagesRootSearchPattern = rootDirectory + "/**/*.cshtml"; // pagesRootSearchPattern will miss _ViewImports outside the RootDirectory despite these influencing // compilation. e.g. when RootDirectory = /Dir1/Dir2, the search pattern will ignore changes to // [/_ViewImports.cshtml, /Dir1/_ViewImports.cshtml]. We need to additionally account for these. var importFileAtPagesRoot = rootDirectory + "/" + templateEngine.Options.ImportsFileName; var additionalImportFilePaths = templateEngine.GetImportItems(importFileAtPagesRoot) .Select(item => item.FilePath); if (razorPagesOptions.Value.AllowAreas) { // Search pattern that matches all cshtml files under the Pages AreaRootDirectory var areaRootSearchPattern = "/Areas/**/*.cshtml"; var importFileAtAreaPagesRoot = $"/Areas/{templateEngine.Options.ImportsFileName}"; var importPathsOutsideAreaPagesRoot = templateEngine.GetImportItems(importFileAtAreaPagesRoot) .Select(item => item.FilePath); additionalImportFilePaths = additionalImportFilePaths .Concat(importPathsOutsideAreaPagesRoot) .Distinct(StringComparer.OrdinalIgnoreCase); _searchPatterns = new[] { pagesRootSearchPattern, areaRootSearchPattern }; } else { _searchPatterns = new[] { pagesRootSearchPattern, }; } _additionalFilesToTrack = additionalImportFilePaths.ToArray(); }
private Task <CompiledViewDescriptor> CreateCacheEntry(string normalizedPath) { TaskCompletionSource <CompiledViewDescriptor> compilationTaskSource = null; MemoryCacheEntryOptions cacheEntryOptions; Task <CompiledViewDescriptor> cacheEntry; // Safe races cannot be allowed when compiling Razor pages. To ensure only one compilation request succeeds // per file, we'll lock the creation of a cache entry. Creating the cache entry should be very quick. The // actual work for compiling files happens outside the critical section. lock (_cacheLock) { if (_cache.TryGetValue(normalizedPath, out cacheEntry)) { return(cacheEntry); } cacheEntryOptions = new MemoryCacheEntryOptions(); cacheEntryOptions.ExpirationTokens.Add(_fileProvider.Watch(normalizedPath)); var projectItem = _templateEngine.Project.GetItem(normalizedPath); if (!projectItem.Exists) { cacheEntry = Task.FromResult(new CompiledViewDescriptor { RelativePath = normalizedPath, ExpirationTokens = cacheEntryOptions.ExpirationTokens, }); } else { // A file exists and needs to be compiled. compilationTaskSource = new TaskCompletionSource <CompiledViewDescriptor>(); foreach (var importItem in _templateEngine.GetImportItems(projectItem)) { cacheEntryOptions.ExpirationTokens.Add(_fileProvider.Watch(importItem.FilePath)); } cacheEntry = compilationTaskSource.Task; } cacheEntry = _cache.Set(normalizedPath, cacheEntry, cacheEntryOptions); } if (compilationTaskSource != null) { // Indicates that a file was found and needs to be compiled. Debug.Assert(cacheEntryOptions != null); try { var descriptor = CompileAndEmit(normalizedPath); descriptor.ExpirationTokens = cacheEntryOptions.ExpirationTokens; compilationTaskSource.SetResult(descriptor); } catch (Exception ex) { compilationTaskSource.SetException(ex); } } return(cacheEntry); }