public async Task CompileAsync_PrecompiledViewWithoutAnyChecksum_DoesNotSupportRecompilation() { // Arrange var path = "/Views/Home/Index.cshtml"; var fileProvider = new TestFileProvider(); var fileInfo = fileProvider.AddFile(path, "some content"); var precompiledView = new CompiledViewDescriptor { RelativePath = path, Item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", path, new object[] { }), }; var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); // Act - 1 var result = await viewCompiler.CompileAsync(path); // Assert - 1 Assert.Same(precompiledView, result); // Act - 2 fileProvider.Watch(path); fileProvider.GetChangeToken(path).HasChanged = true; result = await viewCompiler.CompileAsync(path); // Assert - 2 Assert.Same(precompiledView, result); // This view doesn't have checksums so it can't be recompiled. Assert.Null(result.ExpirationTokens); }
public async Task CompileAsync_PrecompiledViewWithChecksum_UsesPrecompiledViewWhenChecksumIsMatch() { // Arrange var path = "/Views/Home/Index.cshtml"; var fileProvider = new TestFileProvider(); var fileInfo = fileProvider.AddFile(path, "some content"); var precompiledView = new CompiledViewDescriptor { RelativePath = path, Item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", path, new object[] { new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), path), }), }; var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); // Act var result = await viewCompiler.CompileAsync(path); // Assert Assert.Same(precompiledView.Item, result.Item); // This view has checksums so it should also have tokens Assert.Collection( result.ExpirationTokens, token => Assert.Same(fileProvider.GetChangeToken(path), token)); }
public void CreateFactory_ProducesDelegateThatSetsPagePath() { // Arrange var relativePath = "/file-exists"; var descriptor = new CompiledViewDescriptor { RelativePath = relativePath, Item = TestRazorCompiledItem.CreateForView(typeof(TestRazorPage), relativePath), ExpirationTokens = Array.Empty <IChangeToken>(), }; var viewCompiler = new Mock <IViewCompiler>(); viewCompiler .Setup(f => f.CompileAsync(It.IsAny <string>())) .ReturnsAsync(descriptor); var factoryProvider = new DefaultRazorPageFactoryProvider(GetCompilerProvider(viewCompiler.Object)); // Act var result = factoryProvider.CreateFactory(relativePath); // Assert Assert.True(result.Success); var actual = result.RazorPageFactory(); Assert.Equal("/file-exists", actual.Path); }
public void CreateFactory_ReturnsViewDescriptor_ForSuccessfulResults() { // Arrange var relativePath = "/file-exists"; var expirationTokens = new[] { Mock.Of <IChangeToken>(), Mock.Of <IChangeToken>(), }; var descriptor = new CompiledViewDescriptor { RelativePath = relativePath, Item = TestRazorCompiledItem.CreateForView(typeof(TestRazorPage), relativePath), ExpirationTokens = expirationTokens, }; var compilerCache = new Mock <IViewCompiler>(); compilerCache .Setup(f => f.CompileAsync(It.IsAny <string>())) .ReturnsAsync(descriptor); var factoryProvider = new DefaultRazorPageFactoryProvider(GetCompilerProvider(compilerCache.Object)); // Act var result = factoryProvider.CreateFactory(relativePath); // Assert Assert.True(result.Success); Assert.Equal(expirationTokens, descriptor.ExpirationTokens); }
public void CreateFactory_ReturnsViewDescriptor_ForUnsuccessfulResults() { // Arrange var path = "/file-does-not-exist"; var expirationTokens = new[] { Mock.Of <IChangeToken>(), Mock.Of <IChangeToken>(), }; var descriptor = new CompiledViewDescriptor { RelativePath = path, ExpirationTokens = expirationTokens, }; var compilerCache = new Mock <IViewCompiler>(); compilerCache .Setup(f => f.CompileAsync(It.IsAny <string>())) .ReturnsAsync(descriptor); var factoryProvider = new DefaultRazorPageFactoryProvider(GetCompilerProvider(compilerCache.Object)); // Act var result = factoryProvider.CreateFactory(path); // Assert Assert.False(result.Success); Assert.Same(descriptor, result.ViewDescriptor); }
public virtual void PopulateFeature(IEnumerable <ApplicationPart> parts, ViewsFeature feature) { if (ActiveWidgetTemplates == null) { ActiveWidgetTemplates = new HashSet <string>(); foreach (var item in feature.ViewDescriptors) { string name = Path.GetFileName(item.RelativePath); if (name.StartsWith("Widget.") && !ActiveWidgetTemplates.Contains(name)) { ActiveWidgetTemplates.Add(name); } } } var knownIdentifiers = new HashSet <string>(StringComparer.OrdinalIgnoreCase); var attributes = GetViewAttributesLegacy(Assembly); foreach (var item in attributes) { var descriptor = new CompiledViewDescriptor(item); if (knownIdentifiers.Add(descriptor.RelativePath)) { string name = Path.GetFileName(descriptor.RelativePath); if (name.StartsWith("Widget.") && !ActiveWidgetTemplates.Contains(name)) { ActiveWidgetTemplates.Add(name); } feature.ViewDescriptors.Add(descriptor); } } }
// Token: 0x0600017E RID: 382 RVA: 0x00006EDC File Offset: 0x000050DC private Task <CompiledViewDescriptor> OnCacheMiss(string normalizedPath) { object cacheLock = this._cacheLock; ViewCompilerWorkItem viewCompilerWorkItem; MemoryCacheEntryOptions memoryCacheEntryOptions; TaskCompletionSource <CompiledViewDescriptor> taskCompletionSource; lock (cacheLock) { Task <CompiledViewDescriptor> result; if (CacheExtensions.TryGetValue <Task <CompiledViewDescriptor> >(this._cache, normalizedPath, out result)) { return(result); } CompiledViewDescriptor precompiledView; if (this._precompiledViews.TryGetValue(normalizedPath, out precompiledView)) { this._logger.ViewCompilerLocatedCompiledViewForPath(normalizedPath); viewCompilerWorkItem = this.CreatePrecompiledWorkItem(normalizedPath, precompiledView); } else { viewCompilerWorkItem = this.CreateRuntimeCompilationWorkItem(normalizedPath); } memoryCacheEntryOptions = new MemoryCacheEntryOptions(); for (int i = 0; i < viewCompilerWorkItem.ExpirationTokens.Count; i++) { memoryCacheEntryOptions.ExpirationTokens.Add(viewCompilerWorkItem.ExpirationTokens[i]); } taskCompletionSource = new TaskCompletionSource <CompiledViewDescriptor>(); if (!viewCompilerWorkItem.SupportsCompilation) { taskCompletionSource.SetResult(viewCompilerWorkItem.Descriptor); } _cacheKeyList.Add(normalizedPath); CacheExtensions.Set <Task <CompiledViewDescriptor> >(this._cache, normalizedPath, taskCompletionSource.Task, memoryCacheEntryOptions); } if (viewCompilerWorkItem.SupportsCompilation) { CompiledViewDescriptor descriptor = viewCompilerWorkItem.Descriptor; if (((descriptor != null) ? descriptor.Item : null) != null && ChecksumValidator.IsItemValid(this._projectEngine.FileSystem, viewCompilerWorkItem.Descriptor.Item)) { taskCompletionSource.SetResult(viewCompilerWorkItem.Descriptor); return(taskCompletionSource.Task); } this._logger.ViewCompilerInvalidingCompiledFile(viewCompilerWorkItem.NormalizedPath); try { CompiledViewDescriptor compiledViewDescriptor = this.CompileAndEmit(normalizedPath); compiledViewDescriptor.ExpirationTokens = memoryCacheEntryOptions.ExpirationTokens; taskCompletionSource.SetResult(compiledViewDescriptor); } catch (Exception exception) { taskCompletionSource.SetException(exception); } } return(taskCompletionSource.Task); }
public void PopulateFeature(IEnumerable <ApplicationPart> parts, ViewsFeature feature) { foreach (var item in this.compiledItems) { var descriptor = new CompiledViewDescriptor(item); feature.ViewDescriptors.Add(descriptor); } }
static bool IsRazorPage(CompiledViewDescriptor viewDescriptor) { if (viewDescriptor.Item != null) { return(viewDescriptor.Item.Kind == RazorPageDocumentKind); } return(false); }
public void GetRouteTemplate_ReturnsNull_IfAttributeDoesNotExist() { // Arrange var descriptor = new CompiledViewDescriptor(TestRazorCompiledItem.CreateForPage("/Pages/About.cshtml")); // Act var result = CompiledPageRouteModelProvider.GetRouteTemplate(descriptor); // Assert Assert.Null(result); }
internal static string?GetRouteTemplate(CompiledViewDescriptor viewDescriptor) { if (viewDescriptor.Item != null) { return(viewDescriptor.Item.Metadata .OfType <RazorCompiledItemMetadataAttribute>() .FirstOrDefault(f => f.Key == RouteTemplateKey) ?.Value); } return(null); }
private HashSet <string> GetAvailableTemplates() { HashSet <string> templates = new HashSet <string>(); var attributes = new RazorCompiledItemLoader().LoadItems(this.GetType().Assembly); foreach (var item in attributes) { var descriptor = new CompiledViewDescriptor(item); string name = Path.GetFileName(descriptor.RelativePath); templates.Add(name); } return(templates); }
private Task <CompiledViewDescriptor> OnCacheMiss(string normalizedPath) { TaskCompletionSource <CompiledViewDescriptor> taskSource; lock (_cacheLock) { // Double-checked locking to handle a possible race. if (_cache.TryGetValue(normalizedPath, out Task <CompiledViewDescriptor> result)) { return(result); } var razorParts = _partManager.ApplicationParts.OfType <CompiledRazorModulesAssemblyPart>().ToList(); foreach (var razorPart in razorParts) { var provider = razorPart as IRazorCompiledItemProvider; var razorCompiledItems = provider.CompiledItems; var item = razorCompiledItems.FirstOrDefault(item => normalizedPath.Equals(GetNormalizedPath(item.Identifier))); if (item != null) { taskSource = new TaskCompletionSource <CompiledViewDescriptor>(creationOptions: TaskCreationOptions.RunContinuationsAsynchronously); var descriptor = new CompiledViewDescriptor(item) { ExpirationTokens = new List <IChangeToken>() { _moduleChangeProvider.GetChangeToken(razorPart.EntryAssemblyPath) } }; // At this point, we've decided what to do - but we should create the cache entry and // release the lock first. var cacheEntryOptions = new MemoryCacheEntryOptions(); for (var i = 0; i < descriptor.ExpirationTokens.Count; i++) { cacheEntryOptions.ExpirationTokens.Add(descriptor.ExpirationTokens[i]); } var task = _cache.Set(descriptor.RelativePath, Task.FromResult(descriptor), cacheEntryOptions); return(task); } } } // Entry does not exist. Attempt to create one. _logger.ViewCompilerCouldNotFindFileAtPath(normalizedPath); return(Task.FromResult(new CompiledViewDescriptor { RelativePath = normalizedPath, ExpirationTokens = Array.Empty <IChangeToken>(), })); }
public virtual void PopulateFeature(IEnumerable <ApplicationPart> parts, ViewsFeature feature) { var knownIdentifiers = new HashSet <string>(StringComparer.OrdinalIgnoreCase); var attributes = GetViewAttributesLegacy(Assembly); foreach (var item in attributes) { var descriptor = new CompiledViewDescriptor(item); if (knownIdentifiers.Add(descriptor.RelativePath)) { feature.ViewDescriptors.Add(descriptor); } } }
private IList <IChangeToken> GetExpirationTokens(CompiledViewDescriptor precompiledView) { var checksums = precompiledView.Item.GetChecksumMetadata(); var expirationTokens = new List <IChangeToken>(checksums.Count); for (var i = 0; i < checksums.Count; i++) { // We rely on Razor to provide the right set of checksums. Trust the compiler, it has to do a good job, // so it probably will. expirationTokens.Add(_fileProvider.Watch(checksums[i].Identifier)); } return(expirationTokens); }
private static IViewCompilerProvider GetCompilerProvider() { var compiledItem = TestRazorCompiledItem.CreateForView(typeof(object), "/Views/Index.cshtml"); var descriptor = new CompiledViewDescriptor(compiledItem); var compiler = new Mock <IViewCompiler>(); compiler.Setup(c => c.CompileAsync(It.IsAny <string>())) .ReturnsAsync(descriptor); var compilerProvider = new Mock <IViewCompilerProvider>(); compilerProvider.Setup(p => p.GetCompiler()) .Returns(compiler.Object); return(compilerProvider.Object); }
public async Task CompileAsync_PerformsCaseInsensitiveLookupsForCompiledViews(string lookupPath) { // Arrange var path = "/Views/Home/Index.cshtml"; var precompiledView = new CompiledViewDescriptor { RelativePath = path, }; var viewCompiler = GetViewCompiler(compiledViews: new[] { precompiledView }); // Act var result = await viewCompiler.CompileAsync(lookupPath); // Assert Assert.Same(precompiledView, result); }
public async Task CompileAsync_PerformsCaseInsensitiveLookupsForCompiledViews_WithNonNormalizedPaths() { // Arrange var path = "/Views/Home/Index.cshtml"; var compiledView = new CompiledViewDescriptor { RelativePath = path, }; var viewCompiler = GetViewCompiler(compiledViews: new[] { compiledView }); // Act var result = await viewCompiler.CompileAsync("Views\\Home\\Index.cshtml"); // Assert Assert.Same(compiledView, result); }
public void GetRouteTemplate_ReturnsPathFromMetadataAttribute() { // Arrange var expected = "test"; var descriptor = new CompiledViewDescriptor(TestRazorCompiledItem.CreateForPage("/Pages/About.cshtml", metadata: new object[] { new RazorCompiledItemMetadataAttribute("RouteTemplate", expected), })); // Act var result = CompiledPageRouteModelProvider.GetRouteTemplate(descriptor); // Assert Assert.Equal(expected, result); }
protected override CompiledViewDescriptor CompileAndEmit(string relativePath) { CompiledViewDescriptor descriptor = base.CompileAndEmit(relativePath); // The Razor compiler adds attributes to the generated IRazorPage code that provide the relative path of the page // but since Statiq uses "invisible" input path(s) that appear in the physical file system but not the virtual one, // we have to remove the input path from the start of the relative path - otherwise we'll end up looking for nested // views in locations like "/input/input/_foo.cshtml" if (descriptor.RelativePath.EndsWith(relativePath)) { descriptor.RelativePath = relativePath; } return(descriptor); }
private PageRouteModel GetPageRouteModel(string rootDirectory, CompiledViewDescriptor viewDescriptor) { var viewEnginePath = GetRootTrimmedPath(rootDirectory, viewDescriptor.RelativePath); if (viewEnginePath.EndsWith(RazorViewEngine.ViewExtension, StringComparison.OrdinalIgnoreCase)) { viewEnginePath = viewEnginePath.Substring(0, viewEnginePath.Length - RazorViewEngine.ViewExtension.Length); } var model = new PageRouteModel(viewDescriptor.RelativePath, viewEnginePath); var pageAttribute = (RazorPageAttribute)viewDescriptor.ViewAttribute; PageSelectorModel.PopulateDefaults(model, viewEnginePath, pageAttribute.RouteTemplate); return(model); }
internal static string GetRouteTemplate(CompiledViewDescriptor viewDescriptor) { if (viewDescriptor.ViewAttribute != null) { return(((RazorPageAttribute)viewDescriptor.ViewAttribute).RouteTemplate); } if (viewDescriptor.Item != null) { return(viewDescriptor.Item.Metadata .OfType <RazorCompiledItemMetadataAttribute>() .FirstOrDefault(f => f.Key == RazorPageDocumentClassifierPass.RouteTemplateKey) ?.Value); } return(null); }
private static IViewCompilerProvider GetCompilerProvider() { var descriptor = new CompiledViewDescriptor { ViewAttribute = new RazorPageAttribute("/Views/Index.cshtml", typeof(object), null), }; var compiler = new Mock <IViewCompiler>(); compiler.Setup(c => c.CompileAsync(It.IsAny <string>())) .ReturnsAsync(descriptor); var compilerProvider = new Mock <IViewCompilerProvider>(); compilerProvider.Setup(p => p.GetCompiler()) .Returns(compiler.Object); return(compilerProvider.Object); }
private ViewCompilerWorkItem CreatePrecompiledWorkItem(string normalizedPath, CompiledViewDescriptor precompiledView) { // We have a precompiled view - but we're not sure that we can use it yet. // // We need to determine first if we have enough information to 'recompile' this view. If that's the case // we'll create change tokens for all of the files. // // Then we'll attempt to validate if any of those files have different content than the original sources // based on checksums. if (precompiledView.Item == null || !ChecksumValidator.IsRecompilationSupported(precompiledView.Item)) { return(new ViewCompilerWorkItem() { // If we don't have a checksum for the primary source file we can't recompile. SupportsCompilation = false, ExpirationTokens = Array.Empty <IChangeToken>(), // Never expire because we can't recompile. Descriptor = precompiledView, // This will be used as-is. }); } var item = new ViewCompilerWorkItem() { SupportsCompilation = true, Descriptor = precompiledView, // This might be used, if the checksums match. // Used to validate and recompile NormalizedPath = normalizedPath, ExpirationTokens = GetExpirationTokens(precompiledView), }; // We also need to create a new descriptor, because the original one doesn't have expiration tokens on // it. These will be used by the view location cache, which is like an L1 cache for views (this class is // the L2 cache). item.Descriptor = new CompiledViewDescriptor() { ExpirationTokens = item.ExpirationTokens, Item = precompiledView.Item, RelativePath = precompiledView.RelativePath, }; return(item); }
public async Task CompileAsync_ReturnsPrecompiledViews() { // Arrange var path = "/Views/Home/Index.cshtml"; var fileProvider = new TestFileProvider(); var fileInfo = fileProvider.AddFile(path, "some content"); var precompiledView = new CompiledViewDescriptor { RelativePath = path, }; var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); // Act var result = await viewCompiler.CompileAsync(path); // Assert Assert.Same(precompiledView, result); }
public async Task CompileAsync_PerformsCaseInsensitiveLookupsForPrecompiledViews(string lookupPath) { // Arrange var path = "/Views/Home/Index.cshtml"; var fileProvider = new TestFileProvider(); fileProvider.AddFile(path, "some content"); var precompiledView = new CompiledViewDescriptor { RelativePath = path }; var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); // Act var result = await viewCompiler.CompileAsync(lookupPath); // Assert Assert.Same(precompiledView, result); }
public async Task CompileAsync_ReturnsCompiledViews() { // Arrange var path = "/Views/Home/Index.cshtml"; var compiledView = new CompiledViewDescriptor { RelativePath = path, }; var viewCompiler = GetViewCompiler(compiledViews: new[] { compiledView }); // Act var result = await viewCompiler.CompileAsync(path); // Assert Assert.Same(compiledView, result); // This view doesn't have checksums so it can't be recompiled. Assert.Null(compiledView.ExpirationTokens); }
private PageRouteModel GetAreaPageRouteModel(string areaRootDirectory, CompiledViewDescriptor viewDescriptor) { var rootTrimmedPath = GetRootTrimmedPath(areaRootDirectory, viewDescriptor.RelativePath); if (PageSelectorModel.TryParseAreaPath(_pagesOptions, rootTrimmedPath, _logger, out var result)) { var model = new PageRouteModel(viewDescriptor.RelativePath, result.viewEnginePath) { RouteValues = { ["area"] = result.areaName }, }; var pageAttribute = (RazorPageAttribute)viewDescriptor.ViewAttribute; PageSelectorModel.PopulateDefaults(model, result.pageRoute, pageAttribute.RouteTemplate); return(model); } // We were unable to parse the path to match the format we expect /Areas/AreaName/Pages/PagePath.cshtml return(null); }
public async Task CompileAsync_DoesNotRecompile_IfFileTriggerWasSetForPrecompiledView() { // Arrange var path = "/Views/Home/Index.cshtml"; var fileProvider = new TestFileProvider(); var fileInfo = fileProvider.AddFile(path, "some content"); var precompiledView = new CompiledViewDescriptor { RelativePath = path, }; var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); // Act fileProvider.Watch(path); fileProvider.GetChangeToken(path).HasChanged = true; var result = await viewCompiler.CompileAsync(path); // Assert Assert.Same(precompiledView, result); }
public async Task CompileAsync_PrecompiledViewWithChecksum_CanRecompileWhenViewImportChanges() { // Arrange var path = "/Views/Home/Index.cshtml"; var importPath = "/Views/_ViewImports.cshtml"; var fileProvider = new TestFileProvider(); var fileInfo = fileProvider.AddFile(path, "some content"); var importFileInfo = fileProvider.AddFile(importPath, "some import"); var expected2 = new CompiledViewDescriptor(); var precompiledView = new CompiledViewDescriptor { RelativePath = path, Item = new TestRazorCompiledItem(typeof(string), "mvc.1.0.view", path, new object[] { new RazorSourceChecksumAttribute("SHA1", GetChecksum("some content"), path), new RazorSourceChecksumAttribute("SHA1", GetChecksum("some import"), importPath), }), }; var viewCompiler = GetViewCompiler(fileProvider, precompiledViews: new[] { precompiledView }); viewCompiler.AllowRecompilingViewsOnFileChange = true; // Act - 1 var result = await viewCompiler.CompileAsync(path); // Assert - 1 Assert.Same(precompiledView.Item, result.Item); // Act - 2 importFileInfo.Content = "some import changed"; fileProvider.GetChangeToken(importPath).HasChanged = true; viewCompiler.Compile = _ => expected2; result = await viewCompiler.CompileAsync(path); // Assert - 2 Assert.Same(expected2, result); }