parent
d2c3985cd6
commit
70ce04ff21
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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": {},
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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-*"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue