Example #1
0
        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";
        }
Example #2
0
        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);
        }