parent
1dc4b28bbe
commit
304000095a
|
|
@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
|
|
||||||
foreach (var item in views)
|
foreach (var item in views)
|
||||||
{
|
{
|
||||||
var cacheEntry = new CompilerCacheResult(item.Key, new CompilationResult(item.Value));
|
var cacheEntry = new CompilerCacheResult(item.Key, new CompilationResult(item.Value), isPrecompiled: true);
|
||||||
_cache.Set(GetNormalizedPath(item.Key), Task.FromResult(cacheEntry));
|
_cache.Set(GetNormalizedPath(item.Key), Task.FromResult(cacheEntry));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,19 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="CompilerCacheResult"/> with the specified
|
||||||
|
/// <see cref="Compilation.CompilationResult"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="relativePath">Path of the view file relative to the application base.</param>
|
||||||
|
/// <param name="compilationResult">The <see cref="Compilation.CompilationResult"/>.</param>
|
||||||
|
/// <param name="isPrecompiled"><c>true</c> if the view is precompiled, <c>false</c> otherwise.</param>
|
||||||
|
public CompilerCacheResult(string relativePath, CompilationResult compilationResult, bool isPrecompiled)
|
||||||
|
: this(relativePath, compilationResult, EmptyArray<IChangeToken>.Instance)
|
||||||
|
{
|
||||||
|
IsPrecompiled = isPrecompiled;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of <see cref="CompilerCacheResult"/> with the specified
|
/// Initializes a new instance of <see cref="CompilerCacheResult"/> with the specified
|
||||||
/// <see cref="Compilation.CompilationResult"/>.
|
/// <see cref="Compilation.CompilationResult"/>.
|
||||||
|
|
@ -51,9 +64,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
|
|
||||||
var propertyBindExpression = Expression.Bind(pathProperty, Expression.Constant(relativePath));
|
var propertyBindExpression = Expression.Bind(pathProperty, Expression.Constant(relativePath));
|
||||||
var objectInitializeExpression = Expression.MemberInit(newExpression, propertyBindExpression);
|
var objectInitializeExpression = Expression.MemberInit(newExpression, propertyBindExpression);
|
||||||
PageFactory = Expression
|
PageFactory = Expression
|
||||||
.Lambda<Func<IRazorPage>>(objectInitializeExpression)
|
.Lambda<Func<IRazorPage>>(objectInitializeExpression)
|
||||||
.Compile();
|
.Compile();
|
||||||
|
IsPrecompiled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -71,6 +85,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
|
|
||||||
ExpirationTokens = expirationTokens;
|
ExpirationTokens = expirationTokens;
|
||||||
PageFactory = null;
|
PageFactory = null;
|
||||||
|
IsPrecompiled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -87,5 +102,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
/// Gets a delegate that creates an instance of the <see cref="IRazorPage"/>.
|
/// Gets a delegate that creates an instance of the <see cref="IRazorPage"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<IRazorPage> PageFactory { get; }
|
public Func<IRazorPage> PageFactory { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value that determines if the view is precompiled.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPrecompiled { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -62,7 +62,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
var result = CompilerCache.GetOrAdd(relativePath, _compileDelegate);
|
var result = CompilerCache.GetOrAdd(relativePath, _compileDelegate);
|
||||||
if (result.Success)
|
if (result.Success)
|
||||||
{
|
{
|
||||||
return new RazorPageFactoryResult(result.PageFactory, result.ExpirationTokens);
|
return new RazorPageFactoryResult(result.PageFactory, result.ExpirationTokens, result.IsPrecompiled);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
|
|
||||||
private static readonly Action<ILogger, string, string, Exception> _viewLookupCacheMiss;
|
private static readonly Action<ILogger, string, string, Exception> _viewLookupCacheMiss;
|
||||||
private static readonly Action<ILogger, string, string, Exception> _viewLookupCacheHit;
|
private static readonly Action<ILogger, string, string, Exception> _viewLookupCacheHit;
|
||||||
|
private static readonly Action<ILogger, string, Exception> _precompiledViewFound;
|
||||||
|
|
||||||
static MvcRazorLoggerExtensions()
|
static MvcRazorLoggerExtensions()
|
||||||
{
|
{
|
||||||
|
|
@ -42,6 +43,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
2,
|
2,
|
||||||
"View lookup cache hit for view '{ViewName}' in controller '{ControllerName}'.");
|
"View lookup cache hit for view '{ViewName}' in controller '{ControllerName}'.");
|
||||||
|
|
||||||
|
_precompiledViewFound = LoggerMessage.Define<string>(
|
||||||
|
LogLevel.Debug,
|
||||||
|
3,
|
||||||
|
"Using precompiled view for '{RelativePath}'.");
|
||||||
|
|
||||||
_generatedCodeToAssemblyCompilationStart = LoggerMessage.Define<string>(
|
_generatedCodeToAssemblyCompilationStart = LoggerMessage.Define<string>(
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
1,
|
1,
|
||||||
|
|
@ -79,6 +85,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
_viewLookupCacheHit(logger, viewName, controllerName, null);
|
_viewLookupCacheHit(logger, viewName, controllerName, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void PrecompiledViewFound(this ILogger logger, string relativePath)
|
||||||
|
{
|
||||||
|
_precompiledViewFound(logger, relativePath, null);
|
||||||
|
}
|
||||||
|
|
||||||
public static void GeneratedCodeToAssemblyCompilationStart(this ILogger logger, string filePath)
|
public static void GeneratedCodeToAssemblyCompilationStart(this ILogger logger, string filePath)
|
||||||
{
|
{
|
||||||
_generatedCodeToAssemblyCompilationStart(logger, filePath, null);
|
_generatedCodeToAssemblyCompilationStart(logger, filePath, null);
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
|
|
||||||
ExpirationTokens = expirationTokens;
|
ExpirationTokens = expirationTokens;
|
||||||
RazorPageFactory = null;
|
RazorPageFactory = null;
|
||||||
|
IsPrecompiled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -37,6 +38,21 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
public RazorPageFactoryResult(
|
public RazorPageFactoryResult(
|
||||||
Func<IRazorPage> razorPageFactory,
|
Func<IRazorPage> razorPageFactory,
|
||||||
IList<IChangeToken> expirationTokens)
|
IList<IChangeToken> expirationTokens)
|
||||||
|
: this(razorPageFactory, expirationTokens, isPrecompiled: false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="RazorPageFactoryResult"/> with the
|
||||||
|
/// specified <see cref="IRazorPage"/> factory.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="razorPageFactory">The <see cref="IRazorPage"/> factory.</param>
|
||||||
|
/// <param name="expirationTokens">One or more <see cref="IChangeToken"/> instances.</param>
|
||||||
|
/// <param name="isPrecompiled"><c>true</c> if the view is precompiled, <c>false</c> otherwise.</param>
|
||||||
|
public RazorPageFactoryResult(
|
||||||
|
Func<IRazorPage> razorPageFactory,
|
||||||
|
IList<IChangeToken> expirationTokens,
|
||||||
|
bool isPrecompiled)
|
||||||
{
|
{
|
||||||
if (razorPageFactory == null)
|
if (razorPageFactory == null)
|
||||||
{
|
{
|
||||||
|
|
@ -50,6 +66,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
|
|
||||||
RazorPageFactory = razorPageFactory;
|
RazorPageFactory = razorPageFactory;
|
||||||
ExpirationTokens = expirationTokens;
|
ExpirationTokens = expirationTokens;
|
||||||
|
IsPrecompiled = isPrecompiled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -68,5 +85,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
/// Gets a value that determines if the page was successfully located.
|
/// Gets a value that determines if the page was successfully located.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Success => RazorPageFactory != null;
|
public bool Success => RazorPageFactory != null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value that determines if the view is precompiled.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPrecompiled { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -395,7 +395,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
return ViewLookupCache.Set<ViewLocationCacheResult>(cacheKey, cacheResult, cacheEntryOptions);
|
return ViewLookupCache.Set<ViewLocationCacheResult>(cacheKey, cacheResult, cacheEntryOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ViewLocationCacheResult CreateCacheResult(
|
// Internal for unit testing
|
||||||
|
internal ViewLocationCacheResult CreateCacheResult(
|
||||||
HashSet<IChangeToken> expirationTokens,
|
HashSet<IChangeToken> expirationTokens,
|
||||||
string relativePath,
|
string relativePath,
|
||||||
bool isMainPage)
|
bool isMainPage)
|
||||||
|
|
@ -415,6 +416,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
||||||
var viewStartPages = isMainPage ?
|
var viewStartPages = isMainPage ?
|
||||||
GetViewStartPages(relativePath, expirationTokens) :
|
GetViewStartPages(relativePath, expirationTokens) :
|
||||||
EmptyArray<ViewLocationCacheItem>.Instance;
|
EmptyArray<ViewLocationCacheItem>.Instance;
|
||||||
|
if (factoryResult.IsPrecompiled)
|
||||||
|
{
|
||||||
|
_logger.PrecompiledViewFound(relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
return new ViewLocationCacheResult(
|
return new ViewLocationCacheResult(
|
||||||
new ViewLocationCacheItem(factoryResult.RazorPageFactory, relativePath),
|
new ViewLocationCacheItem(factoryResult.RazorPageFactory, relativePath),
|
||||||
|
|
|
||||||
|
|
@ -241,6 +241,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result.Success);
|
Assert.True(result.Success);
|
||||||
|
Assert.True(result.IsPrecompiled);
|
||||||
Assert.IsType<PreCompile>(result.PageFactory());
|
Assert.IsType<PreCompile>(result.PageFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1283,6 +1283,35 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
|
||||||
Assert.Equal(expected, result.SearchedLocations);
|
Assert.Equal(expected, result.SearchedLocations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateCacheResult_LogsPrecompiledViewFound()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var sink = new TestSink();
|
||||||
|
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
||||||
|
|
||||||
|
var relativePath = "/Views/Foo/details.cshtml";
|
||||||
|
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||||
|
pageFactory
|
||||||
|
.Setup(p => p.CreateFactory(relativePath))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => Mock.Of<IRazorPage>(), new IChangeToken[0], isPrecompiled: true))
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var viewEngine = new RazorViewEngine(
|
||||||
|
pageFactory.Object,
|
||||||
|
Mock.Of<IRazorPageActivator>(),
|
||||||
|
new HtmlTestEncoder(),
|
||||||
|
GetOptionsAccessor(expanders: null),
|
||||||
|
loggerFactory);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = viewEngine.CreateCacheResult(null, relativePath, false);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var logMessage = Assert.Single(sink.Writes);
|
||||||
|
Assert.Equal($"Using precompiled view for '{relativePath}'.", logMessage.State.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("/Test-View.cshtml")]
|
[InlineData("/Test-View.cshtml")]
|
||||||
[InlineData("~/Test-View.CSHTML")]
|
[InlineData("~/Test-View.CSHTML")]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue