From 70ce04ff2113e0bef14a830bae281d23bed052ab Mon Sep 17 00:00:00 2001 From: Pranav K Date: Fri, 6 Feb 2015 18:37:08 -0800 Subject: [PATCH] Results of parsing _ViewStart files should be cached Fixes #1016 --- .../Directives/ChunkInheritanceUtility.cs | 36 ++-- .../Directives/DefaultCodeTreeCache.cs | 63 +++++++ .../Directives/ICodeTreeCache.cs | 27 +++ .../MvcRazorHost.cs | 15 +- .../project.json | 3 +- src/Microsoft.AspNet.Mvc.Razor/project.json | 1 - .../project.json | 1 - src/Microsoft.AspNet.Mvc/MvcServices.cs | 11 +- .../Directives/ChunkInheritanceUtilityTest.cs | 15 +- .../Directives/DefaultCodeTreeCacheTest.cs | 169 ++++++++++++++++++ .../InjectChunkVisitorTest.cs | 4 +- .../ModelChunkVisitorTest.cs | 4 +- .../MvcRazorHostTest.cs | 27 +-- 13 files changed, 320 insertions(+), 56 deletions(-) create mode 100644 src/Microsoft.AspNet.Mvc.Razor.Host/Directives/DefaultCodeTreeCache.cs create mode 100644 src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ICodeTreeCache.cs create mode 100644 test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/DefaultCodeTreeCacheTest.cs diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs index 3cd5b7bd6b..f74ac1db77 100644 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ChunkInheritanceUtility.cs @@ -18,25 +18,24 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives /// public class ChunkInheritanceUtility { - private readonly Dictionary _parsedCodeTrees; private readonly MvcRazorHost _razorHost; - private readonly IFileProvider _fileProvider; private readonly IReadOnlyList _defaultInheritedChunks; + private readonly ICodeTreeCache _codeTreeCache; /// /// Initializes a new instance of . /// /// The used to parse _ViewStart pages. - /// The fileProvider that represents the application. + /// that caches _ViewStart + /// instances. /// Sequence of s inherited by default. public ChunkInheritanceUtility([NotNull] MvcRazorHost razorHost, - [NotNull] IFileProvider fileProvider, + [NotNull] ICodeTreeCache codeTreeCache, [NotNull] IReadOnlyList defaultInheritedChunks) { _razorHost = razorHost; - _fileProvider = fileProvider; _defaultInheritedChunks = defaultInheritedChunks; - _parsedCodeTrees = new Dictionary(StringComparer.Ordinal); + _codeTreeCache = codeTreeCache; } /// @@ -50,31 +49,20 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives public IReadOnlyList GetInheritedCodeTrees([NotNull] string pagePath) { var inheritedCodeTrees = new List(); - var templateEngine = new RazorTemplateEngine(_razorHost); foreach (var viewStartPath in ViewStartUtility.GetViewStartLocations(pagePath)) { - CodeTree codeTree; + // viewStartPath contains the app-relative path of the ViewStart. + // Since the parsing of a _ViewStart would cause parent _ViewStarts to be parsed + // we need to ensure the paths are app-relative to allow the GetViewStartLocations + // for the current _ViewStart to succeed. + var codeTree = _codeTreeCache.GetOrAdd(viewStartPath, + fileInfo => ParseViewFile(templateEngine, fileInfo, viewStartPath)); - if (_parsedCodeTrees.TryGetValue(viewStartPath, out codeTree)) + if (codeTree != null) { inheritedCodeTrees.Add(codeTree); } - else - { - var fileInfo = _fileProvider.GetFileInfo(viewStartPath); - if (fileInfo.Exists) - { - // viewStartPath contains the app-relative path of the ViewStart. - // Since the parsing of a _ViewStart would cause parent _ViewStarts to be parsed - // we need to ensure the paths are app-relative to allow the GetViewStartLocations - // for the current _ViewStart to succeed. - codeTree = ParseViewFile(templateEngine, fileInfo, viewStartPath); - _parsedCodeTrees.Add(viewStartPath, codeTree); - - inheritedCodeTrees.Add(codeTree); - } - } } return inheritedCodeTrees; diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/DefaultCodeTreeCache.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/DefaultCodeTreeCache.cs new file mode 100644 index 0000000000..3c854f3af2 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/DefaultCodeTreeCache.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.FileProviders; +using Microsoft.AspNet.Razor.Generator.Compiler; +using Microsoft.Framework.Cache.Memory; + +namespace Microsoft.AspNet.Mvc.Razor.Directives +{ + /// + /// Default implementation of . + /// + public class DefaultCodeTreeCache : ICodeTreeCache + { + private static readonly MemoryCacheOptions MemoryCacheOptions = new MemoryCacheOptions + { + ListenForMemoryPressure = false + }; + private static readonly TimeSpan SlidingExpirationDuration = TimeSpan.FromMinutes(1); + private readonly IFileProvider _fileProvider; + private readonly IMemoryCache _codeTreeCache; + + /// + /// Initializes a new instance of . + /// + /// The application's . + public DefaultCodeTreeCache(IFileProvider fileProvider) + : this(fileProvider, MemoryCacheOptions) + { + } + + // Internal for unit testing + internal DefaultCodeTreeCache(IFileProvider fileProvider, + MemoryCacheOptions options) + { + _fileProvider = fileProvider; + _codeTreeCache = new MemoryCache(options); + } + + /// + public CodeTree GetOrAdd([NotNull] string pagePath, + [NotNull] Func getCodeTree) + { + return _codeTreeCache.GetOrSet(pagePath, getCodeTree, OnCacheMiss); + } + + private CodeTree OnCacheMiss(ICacheSetContext cacheSetContext) + { + var pagePath = cacheSetContext.Key; + var getCodeTree = (Func)cacheSetContext.State; + + // GetOrAdd is invoked for each _ViewStart that might potentially exist in the path. + // We can avoid performing file system lookups for files that do not exist by caching + // negative results and adding a Watch for that file. + cacheSetContext.AddExpirationTrigger(_fileProvider.Watch(pagePath)); + cacheSetContext.SetSlidingExpiration(SlidingExpirationDuration); + + var file = _fileProvider.GetFileInfo(pagePath); + return file.Exists ? getCodeTree(file) : null; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ICodeTreeCache.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ICodeTreeCache.cs new file mode 100644 index 0000000000..081fbc6625 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/Directives/ICodeTreeCache.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.FileProviders; +using Microsoft.AspNet.Razor.Generator.Compiler; + +namespace Microsoft.AspNet.Mvc.Razor.Directives +{ + /// + /// A cache for parsed s. + /// + public interface ICodeTreeCache + { + /// + /// Get an existing , or create and add a new one if it is + /// not available in the cache or is expired. + /// + /// The application relative path of the Razor page. + /// A delegate that creates a new . + /// The if a file exists at , + /// null otherwise. + /// The resulting does not contain inherited chunks from _ViewStart or + /// default inherited chunks. + CodeTree GetOrAdd(string pagePath, Func getCodeTree); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs index 1dc32bdad4..b27814a3e7 100644 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs @@ -31,11 +31,10 @@ namespace Microsoft.AspNet.Mvc.Razor new InjectChunk("Microsoft.AspNet.Mvc.IUrlHelper", "Url"), }; - private readonly IFileProvider _fileProvider; - // CodeGenerationContext.DefaultBaseClass is set to MyBaseType. // This field holds the type name without the generic decoration (MyBaseType) private readonly string _baseType; + private readonly ICodeTreeCache _codeTreeCache; private ChunkInheritanceUtility _chunkInheritanceUtility; #if NET45 @@ -44,8 +43,12 @@ namespace Microsoft.AspNet.Mvc.Razor /// . /// /// The path to the application base. + // Note: This constructor is used by tooling and is created once for each + // Razor page that is loaded. Consequently, each loaded page has its own copy of + // the CodeTreeCache, but this ok - having a shared CodeTreeCache per application in tooling + // is problematic to manage. public MvcRazorHost(string root) : - this(new PhysicalFileProvider(root)) + this(new DefaultCodeTreeCache(new PhysicalFileProvider(root))) { } #endif @@ -53,11 +56,11 @@ namespace Microsoft.AspNet.Mvc.Razor /// Initializes a new instance of using the specified . /// /// A rooted at the application base path. - public MvcRazorHost(IFileProvider fileProvider) + public MvcRazorHost(ICodeTreeCache codeTreeCache) : base(new CSharpRazorCodeLanguage()) { - _fileProvider = fileProvider; _baseType = BaseType; + _codeTreeCache = codeTreeCache; TagHelperDescriptorResolver = new TagHelperDescriptorResolver(); DefaultBaseClass = BaseType + "<" + DefaultModel + ">"; @@ -164,7 +167,7 @@ namespace Microsoft.AspNet.Mvc.Razor if (_chunkInheritanceUtility == null) { // This needs to be lazily evaluated to support DefaultInheritedChunks being virtual. - _chunkInheritanceUtility = new ChunkInheritanceUtility(this, _fileProvider, DefaultInheritedChunks); + _chunkInheritanceUtility = new ChunkInheritanceUtility(this, _codeTreeCache, DefaultInheritedChunks); } return _chunkInheritanceUtility; diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/project.json b/src/Microsoft.AspNet.Mvc.Razor.Host/project.json index 182ab8cdc2..6efb390285 100644 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/project.json +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/project.json @@ -7,7 +7,8 @@ "dependencies": { "Microsoft.AspNet.FileProviders": "1.0.0-*", "Microsoft.AspNet.Mvc.Common": { "version": "6.0.0-*", "type": "build" }, - "Microsoft.AspNet.Razor.Runtime": "4.0.0-*" + "Microsoft.AspNet.Razor.Runtime": "4.0.0-*", + "Microsoft.Framework.Cache.Memory": "1.0.0-*" }, "frameworks": { "net45": {}, diff --git a/src/Microsoft.AspNet.Mvc.Razor/project.json b/src/Microsoft.AspNet.Mvc.Razor/project.json index 75da75289d..9606627223 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/project.json +++ b/src/Microsoft.AspNet.Mvc.Razor/project.json @@ -9,7 +9,6 @@ "Microsoft.AspNet.Mvc.Core": "6.0.0-*", "Microsoft.AspNet.Mvc.Razor.Host": "6.0.0-*", "Microsoft.CodeAnalysis.CSharp": "1.0.0-rc1-*", - "Microsoft.Framework.Cache.Memory": "1.0.0-*", "Microsoft.Framework.Runtime.Roslyn.Common": "1.0.0-*" }, "frameworks": { diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/project.json b/src/Microsoft.AspNet.Mvc.TagHelpers/project.json index 8ccd9890a1..c32fd00f7b 100644 --- a/src/Microsoft.AspNet.Mvc.TagHelpers/project.json +++ b/src/Microsoft.AspNet.Mvc.TagHelpers/project.json @@ -7,7 +7,6 @@ "dependencies": { "Microsoft.AspNet.Mvc.Common": { "version": "6.0.0-*", "type": "build" }, "Microsoft.AspNet.Mvc.Razor": "6.0.0-*", - "Microsoft.Framework.Cache.Memory": "1.0.0-*", "Microsoft.Framework.Logging.Interfaces": "1.0.0-*", "System.Security.Cryptography.Hashing.Algorithms": "4.0.0-beta-*" }, diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs index d929e1db63..43921f5e99 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServices.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServices.cs @@ -9,10 +9,10 @@ using Microsoft.AspNet.Mvc.Internal; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.OptionDescriptors; using Microsoft.AspNet.Mvc.Razor; +using Microsoft.AspNet.Mvc.Razor.Directives; using Microsoft.AspNet.Mvc.Razor.OptionDescriptors; using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Routing; -using Microsoft.AspNet.Security; using Microsoft.Framework.Cache.Memory; using Microsoft.Framework.ConfigurationModel; using Microsoft.Framework.DependencyInjection; @@ -107,14 +107,15 @@ namespace Microsoft.AspNet.Mvc yield return describe.Transient(); // Caches view locations that are valid for the lifetime of the application. yield return describe.Singleton(); - - // The host is designed to be discarded after consumption and is very inexpensive to initialize. - yield return describe.Transient(serviceProvider => + yield return describe.Singleton(serviceProvider => { var cachedFileProvider = serviceProvider.GetRequiredService>(); - return new MvcRazorHost(cachedFileProvider.Options.FileProvider); + return new DefaultCodeTreeCache(cachedFileProvider.Options.FileProvider); }); + // The host is designed to be discarded after consumption and is very inexpensive to initialize. + yield return describe.Transient(); + // Caches compilation artifacts across the lifetime of the application. yield return describe.Singleton(); diff --git a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs index 565918861c..04202fe34c 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/ChunkInheritanceUtilityTest.cs @@ -30,8 +30,9 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives new InjectChunk("MyTestHtmlHelper", "Html"), new UsingChunk { Namespace = "AppNamespace.Model" }, }; - var host = new MvcRazorHost(fileProvider); - var utility = new ChunkInheritanceUtility(host, fileProvider, defaultChunks); + var cache = new DefaultCodeTreeCache(fileProvider); + var host = new MvcRazorHost(cache); + var utility = new ChunkInheritanceUtility(host, cache, defaultChunks); // Act var codeTrees = utility.GetInheritedCodeTrees(@"Views\home\Index.cshtml"); @@ -70,13 +71,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives fileProvider.AddFile(@"_ViewStart.cs", string.Empty); fileProvider.AddFile(@"Views\_Layout.cshtml", string.Empty); fileProvider.AddFile(@"Views\home\_not-viewstart.cshtml", string.Empty); - var host = new MvcRazorHost(fileProvider); + var cache = new DefaultCodeTreeCache(fileProvider); + var host = new MvcRazorHost(cache); var defaultChunks = new Chunk[] { new InjectChunk("MyTestHtmlHelper", "Html"), new UsingChunk { Namespace = "AppNamespace.Model" }, }; - var utility = new ChunkInheritanceUtility(host, fileProvider, defaultChunks); + var utility = new ChunkInheritanceUtility(host, cache, defaultChunks); // Act var codeTrees = utility.GetInheritedCodeTrees(@"Views\home\Index.cshtml"); @@ -92,7 +94,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives var fileProvider = new TestFileProvider(); fileProvider.AddFile(@"Views\_ViewStart.cshtml", "@inject DifferentHelper Html"); - var host = new MvcRazorHost(fileProvider); + var cache = new DefaultCodeTreeCache(fileProvider); + var host = new MvcRazorHost(cache); var defaultChunks = new Chunk[] { new InjectChunk("MyTestHtmlHelper", "Html"), @@ -117,7 +120,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives } }; - var utility = new ChunkInheritanceUtility(host, fileProvider, defaultChunks); + var utility = new ChunkInheritanceUtility(host, cache, defaultChunks); var codeTree = new CodeTree(); // Act diff --git a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/DefaultCodeTreeCacheTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/DefaultCodeTreeCacheTest.cs new file mode 100644 index 0000000000..1c016a25cc --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/Directives/DefaultCodeTreeCacheTest.cs @@ -0,0 +1,169 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using Microsoft.AspNet.Mvc.Razor.Directives; +using Microsoft.AspNet.Razor.Generator.Compiler; +using Microsoft.Framework.Cache.Memory; +using Microsoft.Framework.Cache.Memory.Infrastructure; +using Moq; +using Xunit; + +namespace Microsoft.AspNet.Mvc.Razor.Host.Directives +{ + public class CodeTreeCacheTest + { + [Fact] + public void GetOrAdd_ReturnsCachedEntriesOnSubsequentCalls() + { + // Arrange + var path = @"Views\_ViewStart.cshtml"; + var mockFileProvider = new Mock { CallBase = true }; + var fileProvider = mockFileProvider.Object; + fileProvider.AddFile(path, "test content"); + var codeTreeCache = new DefaultCodeTreeCache(fileProvider); + var expected = new CodeTree(); + + // Act + var result1 = codeTreeCache.GetOrAdd(path, fileInfo => expected); + var result2 = codeTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); }); + + // Assert + Assert.Same(expected, result1); + Assert.Same(expected, result2); + mockFileProvider.Verify(f => f.GetFileInfo(It.IsAny()), Times.Once()); + } + + [Fact] + public void GetOrAdd_ReturnsNullValues_IfFileDoesNotExistInFileProvider() + { + // Arrange + var path = @"Views\_ViewStart.cshtml"; + var mockFileProvider = new Mock { CallBase = true }; + var fileProvider = mockFileProvider.Object; + var codeTreeCache = new DefaultCodeTreeCache(fileProvider); + var expected = new CodeTree(); + + // Act + var result1 = codeTreeCache.GetOrAdd(path, fileInfo => expected); + var result2 = codeTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); }); + + // Assert + Assert.Null(result1); + Assert.Null(result2); + mockFileProvider.Verify(f => f.GetFileInfo(It.IsAny()), Times.Once()); + } + + [Fact] + public void GetOrAdd_UpdatesCache_IfFileExpirationTriggerExpires() + { + // Arrange + var path = @"Views\Home\_ViewStart.cshtml"; + var fileProvider = new TestFileProvider(); + fileProvider.AddFile(path, "test content"); + var codeTreeCache = new DefaultCodeTreeCache(fileProvider); + var expected1 = new CodeTree(); + var expected2 = new CodeTree(); + + // Act 1 + var result1 = codeTreeCache.GetOrAdd(path, fileInfo => expected1); + + // Assert 1 + Assert.Same(expected1, result1); + + // Act 2 + fileProvider.GetTrigger(path).IsExpired = true; + var result2 = codeTreeCache.GetOrAdd(path, fileInfo => expected2); + + // Assert 2 + Assert.Same(expected2, result2); + } + + [Fact] + public void GetOrAdd_UpdatesCacheWithNullValue_IfFileWasDeleted() + { + // Arrange + var path = @"Views\Home\_ViewStart.cshtml"; + var fileProvider = new TestFileProvider(); + fileProvider.AddFile(path, "test content"); + var codeTreeCache = new DefaultCodeTreeCache(fileProvider); + var expected1 = new CodeTree(); + + // Act 1 + var result1 = codeTreeCache.GetOrAdd(path, fileInfo => expected1); + + // Assert 1 + Assert.Same(expected1, result1); + + // Act 2 + fileProvider.DeleteFile(path); + fileProvider.GetTrigger(path).IsExpired = true; + var result2 = codeTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); }); + + // Assert 2 + Assert.Null(result2); + } + + [Fact] + public void GetOrAdd_UpdatesCacheWithValue_IfFileWasAdded() + { + // Arrange + var path = @"Views\Home\_ViewStart.cshtml"; + var fileProvider = new TestFileProvider(); + var codeTreeCache = new DefaultCodeTreeCache(fileProvider); + var expected = new CodeTree(); + + // Act 1 + var result1 = codeTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); }); + + // Assert 1 + Assert.Null(result1); + + // Act 2 + fileProvider.AddFile(path, "test content"); + fileProvider.GetTrigger(path).IsExpired = true; + var result2 = codeTreeCache.GetOrAdd(path, fileInfo => expected); + + // Assert 2 + Assert.Same(expected, result2); + } + + [Fact] + public void GetOrAdd_ExpiresEntriesAfterOneMinute() + { + // Arrange + var path = @"Views\Home\_ViewStart.cshtml"; + var fileProvider = new TestFileProvider(); + fileProvider.AddFile(path, "some content"); + var clock = new Mock(); + var utcNow = DateTimeOffset.UtcNow; + clock.SetupGet(c => c.UtcNow) + .Returns(() => utcNow); + var options = new MemoryCacheOptions { Clock = clock.Object }; + var codeTreeCache = new DefaultCodeTreeCache(fileProvider, options); + var codeTree1 = new CodeTree(); + var codeTree2 = new CodeTree(); + + // Act 1 + var result1 = codeTreeCache.GetOrAdd(path, fileInfo => codeTree1); + + // Assert 1 + Assert.Same(codeTree1, result1); + + // Act 2 + utcNow = utcNow.AddSeconds(59); + var result2 = codeTreeCache.GetOrAdd(path, fileInfo => { throw new Exception("Shouldn't be called."); }); + + // Assert 2 + Assert.Same(codeTree1, result2); + + // Act 3 + utcNow = utcNow.AddSeconds(65); + var result3 = codeTreeCache.GetOrAdd(path, fileInfo => codeTree2); + + // Assert 3 + Assert.Same(codeTree2, result3); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/InjectChunkVisitorTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/InjectChunkVisitorTest.cs index 61031fa017..c4a6bcf5af 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/InjectChunkVisitorTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/InjectChunkVisitorTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.AspNet.Mvc.Razor.Directives; using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Generator.Compiler; using Microsoft.AspNet.Razor.Generator.Compiler.CSharp; @@ -145,8 +146,9 @@ MyType1 private static CodeBuilderContext CreateContext() { + var codeTreeCache = new DefaultCodeTreeCache(new TestFileProvider()); return new CodeBuilderContext( - new CodeGeneratorContext(new MvcRazorHost(new TestFileProvider()), + new CodeGeneratorContext(new MvcRazorHost(codeTreeCache), "MyClass", "MyNamespace", string.Empty, diff --git a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/ModelChunkVisitorTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/ModelChunkVisitorTest.cs index d26bbc74b6..566624941e 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/ModelChunkVisitorTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/ModelChunkVisitorTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.AspNet.Mvc.Razor.Directives; using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Generator.Compiler; using Microsoft.AspNet.Razor.Generator.Compiler.CSharp; @@ -101,8 +102,9 @@ Environment.NewLine + private static CodeBuilderContext CreateContext() { + var fileProvider = new TestFileProvider(); return new CodeBuilderContext( - new CodeGeneratorContext(new MvcRazorHost(new TestFileProvider()), + new CodeGeneratorContext(new MvcRazorHost(new DefaultCodeTreeCache(fileProvider)), "MyClass", "MyNamespace", string.Empty, diff --git a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/MvcRazorHostTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/MvcRazorHostTest.cs index ef6972ceb4..ffef6acf74 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Host.Test/MvcRazorHostTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Host.Test/MvcRazorHostTest.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.IO; -using Microsoft.AspNet.FileProviders; +using Microsoft.AspNet.Mvc.Razor.Directives; using Microsoft.AspNet.Razor; using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Generator.Compiler; @@ -19,7 +19,8 @@ namespace Microsoft.AspNet.Mvc.Razor public void MvcRazorHost_EnablesInstrumentationByDefault() { // Arrange - var host = new MvcRazorHost(new TestFileProvider()); + var fileProvider = new TestFileProvider(); + var host = new MvcRazorHost(new DefaultCodeTreeCache(fileProvider)); // Act var instrumented = host.EnableInstrumentation; @@ -32,7 +33,8 @@ namespace Microsoft.AspNet.Mvc.Razor public void MvcRazorHost_GeneratesTagHelperModelExpressionCode_DesignTime() { // Arrange - var host = new MvcRazorHost(new TestFileProvider()) + var fileProvider = new TestFileProvider(); + var host = new MvcRazorHost(new DefaultCodeTreeCache(fileProvider)) { DesignTimeMode = true }; @@ -85,7 +87,8 @@ namespace Microsoft.AspNet.Mvc.Razor public void MvcRazorHost_ParsesAndGeneratesCodeForBasicScenarios(string scenarioName) { // Arrange - var host = new TestMvcRazorHost(new TestFileProvider()); + var fileProvider = new TestFileProvider(); + var host = new TestMvcRazorHost(new DefaultCodeTreeCache(fileProvider)); // Act and Assert RunRuntimeTest(host, scenarioName); @@ -95,7 +98,8 @@ namespace Microsoft.AspNet.Mvc.Razor public void InjectVisitor_GeneratesCorrectLineMappings() { // Arrange - var host = new MvcRazorHost(new TestFileProvider()) + var fileProvider = new TestFileProvider(); + var host = new MvcRazorHost(new DefaultCodeTreeCache(fileProvider)) { DesignTimeMode = true }; @@ -114,7 +118,8 @@ namespace Microsoft.AspNet.Mvc.Razor public void InjectVisitorWithModel_GeneratesCorrectLineMappings() { // Arrange - var host = new MvcRazorHost(new TestFileProvider()) + var fileProvider = new TestFileProvider(); + var host = new MvcRazorHost(new DefaultCodeTreeCache(fileProvider)) { DesignTimeMode = true }; @@ -134,7 +139,8 @@ namespace Microsoft.AspNet.Mvc.Razor public void InjectVisitorWithSemicolon_GeneratesCorrectLineMappings() { // Arrange - var host = new MvcRazorHost(new TestFileProvider()) + var fileProvider = new TestFileProvider(); + var host = new MvcRazorHost(new DefaultCodeTreeCache(fileProvider)) { DesignTimeMode = true }; @@ -156,7 +162,8 @@ namespace Microsoft.AspNet.Mvc.Razor public void ModelVisitor_GeneratesCorrectLineMappings() { // Arrange - var host = new MvcRazorHost(new TestFileProvider()) + var fileProvider = new TestFileProvider(); + var host = new MvcRazorHost(new DefaultCodeTreeCache(fileProvider)) { DesignTimeMode = true }; @@ -252,8 +259,8 @@ namespace Microsoft.AspNet.Mvc.Razor /// private class TestMvcRazorHost : MvcRazorHost { - public TestMvcRazorHost(IFileProvider fileProvider) - : base(fileProvider) + public TestMvcRazorHost(ICodeTreeCache codeTreeCache) + : base(codeTreeCache) { } public override CodeBuilder DecorateCodeBuilder(CodeBuilder incomingBuilder, CodeBuilderContext context)