Results of parsing _ViewStart files should be cached

Fixes #1016
This commit is contained in:
Pranav K 2015-02-06 18:37:08 -08:00
parent d2c3985cd6
commit 70ce04ff21
13 changed files with 320 additions and 56 deletions

View File

@ -18,25 +18,24 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
/// </summary>
public class ChunkInheritanceUtility
{
private readonly Dictionary<string, CodeTree> _parsedCodeTrees;
private readonly MvcRazorHost _razorHost;
private readonly IFileProvider _fileProvider;
private readonly IReadOnlyList<Chunk> _defaultInheritedChunks;
private readonly ICodeTreeCache _codeTreeCache;
/// <summary>
/// Initializes a new instance of <see cref="ChunkInheritanceUtility"/>.
/// </summary>
/// <param name="razorHost">The <see cref="MvcRazorHost"/> used to parse _ViewStart pages.</param>
/// <param name="fileProvider">The fileProvider that represents the application.</param>
/// <param name="codeTreeCache"><see cref="ICodeTreeCache"/> that caches _ViewStart <see cref="CodeTree"/>
/// instances.</param>
/// <param name="defaultInheritedChunks">Sequence of <see cref="Chunk"/>s inherited by default.</param>
public ChunkInheritanceUtility([NotNull] MvcRazorHost razorHost,
[NotNull] IFileProvider fileProvider,
[NotNull] ICodeTreeCache codeTreeCache,
[NotNull] IReadOnlyList<Chunk> defaultInheritedChunks)
{
_razorHost = razorHost;
_fileProvider = fileProvider;
_defaultInheritedChunks = defaultInheritedChunks;
_parsedCodeTrees = new Dictionary<string, CodeTree>(StringComparer.Ordinal);
_codeTreeCache = codeTreeCache;
}
/// <summary>
@ -50,31 +49,20 @@ namespace Microsoft.AspNet.Mvc.Razor.Directives
public IReadOnlyList<CodeTree> GetInheritedCodeTrees([NotNull] string pagePath)
{
var inheritedCodeTrees = new List<CodeTree>();
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;

View File

@ -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
{
/// <summary>
/// Default implementation of <see cref="ICodeTreeCache"/>.
/// </summary>
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;
/// <summary>
/// Initializes a new instance of <see cref="DefaultCodeTreeCache"/>.
/// </summary>
/// <param name="fileProvider">The application's <see cref="IFileProvider"/>.</param>
public DefaultCodeTreeCache(IFileProvider fileProvider)
: this(fileProvider, MemoryCacheOptions)
{
}
// Internal for unit testing
internal DefaultCodeTreeCache(IFileProvider fileProvider,
MemoryCacheOptions options)
{
_fileProvider = fileProvider;
_codeTreeCache = new MemoryCache(options);
}
/// <inheritdoc />
public CodeTree GetOrAdd([NotNull] string pagePath,
[NotNull] Func<IFileInfo, CodeTree> getCodeTree)
{
return _codeTreeCache.GetOrSet(pagePath, getCodeTree, OnCacheMiss);
}
private CodeTree OnCacheMiss(ICacheSetContext cacheSetContext)
{
var pagePath = cacheSetContext.Key;
var getCodeTree = (Func<IFileInfo, CodeTree>)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;
}
}
}

View File

@ -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
{
/// <summary>
/// A cache for parsed <see cref="CodeTree"/>s.
/// </summary>
public interface ICodeTreeCache
{
/// <summary>
/// Get an existing <see cref="CodeTree"/>, or create and add a new one if it is
/// not available in the cache or is expired.
/// </summary>
/// <param name="pagePath">The application relative path of the Razor page.</param>
/// <param name="getCodeTree">A delegate that creates a new <see cref="CodeTree"/>.</param>
/// <returns>The <see cref="CodeTree"/> if a file exists at <paramref name="pagePath"/>,
/// <c>null</c> otherwise.</returns>
/// <remarks>The resulting <see cref="CodeTree"/> does not contain inherited chunks from _ViewStart or
/// default inherited chunks.</remarks>
CodeTree GetOrAdd(string pagePath, Func<IFileInfo, CodeTree> getCodeTree);
}
}

View File

@ -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<dynamic>.
// 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
/// <param name="root"/>.
/// </summary>
/// <param name="root">The path to the application base.</param>
// 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 <see cref="MvcRazorHost"/> using the specified <paramref name="fileProvider"/>.
/// </summary>
/// <param name="fileProvider">A <see cref="IFileProvider"/> rooted at the application base path.</param>
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;

View File

@ -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": {},

View File

@ -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": {

View File

@ -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-*"
},

View File

@ -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<IViewLocationExpanderProvider, DefaultViewLocationExpanderProvider>();
// Caches view locations that are valid for the lifetime of the application.
yield return describe.Singleton<IViewLocationCache, DefaultViewLocationCache>();
// The host is designed to be discarded after consumption and is very inexpensive to initialize.
yield return describe.Transient<IMvcRazorHost>(serviceProvider =>
yield return describe.Singleton<ICodeTreeCache>(serviceProvider =>
{
var cachedFileProvider = serviceProvider.GetRequiredService<IOptions<RazorViewEngineOptions>>();
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<IMvcRazorHost, MvcRazorHost>();
// Caches compilation artifacts across the lifetime of the application.
yield return describe.Singleton<ICompilerCache, CompilerCache>();

View File

@ -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<TModel> 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

View File

@ -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<TestFileProvider> { 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<string>()), Times.Once());
}
[Fact]
public void GetOrAdd_ReturnsNullValues_IfFileDoesNotExistInFileProvider()
{
// Arrange
var path = @"Views\_ViewStart.cshtml";
var mockFileProvider = new Mock<TestFileProvider> { 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<string>()), 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<ISystemClock>();
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);
}
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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
/// </summary>
private class TestMvcRazorHost : MvcRazorHost
{
public TestMvcRazorHost(IFileProvider fileProvider)
: base(fileProvider)
public TestMvcRazorHost(ICodeTreeCache codeTreeCache)
: base(codeTreeCache)
{ }
public override CodeBuilder DecorateCodeBuilder(CodeBuilder incomingBuilder, CodeBuilderContext context)