Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Redesign RazorViewEngine caching
Browse files Browse the repository at this point in the history
Fixes #3337
  • Loading branch information
pranavkm committed Oct 29, 2015
1 parent a69a7a6 commit 3891494
Show file tree
Hide file tree
Showing 19 changed files with 419 additions and 497 deletions.
20 changes: 13 additions & 7 deletions src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilerCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Text;
using Microsoft.AspNet.FileProviders;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Primitives;

namespace Microsoft.AspNet.Mvc.Razor.Compilation
{
Expand Down Expand Up @@ -54,9 +55,10 @@ public CompilerCache(
throw new ArgumentNullException(nameof(precompiledViews));
}

var expirationTokens = new IChangeToken[0];
foreach (var item in precompiledViews)
{
var cacheEntry = new CompilerCacheResult(CompilationResult.Successful(item.Value));
var cacheEntry = new CompilerCacheResult(CompilationResult.Successful(item.Value), expirationTokens);
_cache.Set(GetNormalizedPath(item.Key), cacheEntry);
}
}
Expand Down Expand Up @@ -95,17 +97,18 @@ private CompilerCacheResult CreateCacheEntry(
string normalizedPath,
Func<RelativeFileInfo, CompilationResult> compile)
{
CompilerCacheResult cacheResult;
var fileInfo = _fileProvider.GetFileInfo(normalizedPath);
MemoryCacheEntryOptions cacheEntryOptions;
CompilerCacheResult cacheResult;
CompilerCacheResult cacheResultToCache;
if (!fileInfo.Exists)
{
cacheResultToCache = CompilerCacheResult.FileNotFound;
cacheResult = CompilerCacheResult.FileNotFound;
var expirationToken = _fileProvider.Watch(normalizedPath);
cacheResult = new CompilerCacheResult(new[] { expirationToken });
cacheResultToCache = cacheResult;

cacheEntryOptions = new MemoryCacheEntryOptions();
cacheEntryOptions.AddExpirationToken(_fileProvider.Watch(normalizedPath));
cacheEntryOptions.AddExpirationToken(expirationToken);
}
else
{
Expand All @@ -117,8 +120,11 @@ private CompilerCacheResult CreateCacheEntry(
// UncachedCompilationResult. This type has the generated code as a string property and do not want
// to cache it. We'll instead cache the unwrapped result.
cacheResultToCache = new CompilerCacheResult(
CompilationResult.Successful(compilationResult.CompiledType));
cacheResult = new CompilerCacheResult(compilationResult);
CompilationResult.Successful(compilationResult.CompiledType),
cacheEntryOptions.ExpirationTokens);
cacheResult = new CompilerCacheResult(
compilationResult,
cacheEntryOptions.ExpirationTokens);
}

_cache.Set(normalizedPath, cacheResultToCache, cacheEntryOptions);
Expand Down
32 changes: 21 additions & 11 deletions src/Microsoft.AspNet.Mvc.Razor/Compilation/CompilerCacheResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using Microsoft.Extensions.Primitives;

namespace Microsoft.AspNet.Mvc.Razor.Compilation
{
Expand All @@ -10,38 +12,46 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
/// </summary>
public class CompilerCacheResult
{
/// <summary>
/// Result of <see cref="ICompilerCache"/> when the specified file does not exist in the
/// file system.
/// </summary>
public static CompilerCacheResult FileNotFound { get; } = new CompilerCacheResult();

/// <summary>
/// Initializes a new instance of <see cref="CompilerCacheResult"/> with the specified
/// <see cref="CompilationResult"/>.
/// </summary>
/// <param name="compilationResult">The <see cref="Compilation.CompilationResult"/> </param>
public CompilerCacheResult(CompilationResult compilationResult)
public CompilerCacheResult(CompilationResult compilationResult, IList<IChangeToken> expirationTokens)
{
if (compilationResult == null)
{
throw new ArgumentNullException(nameof(compilationResult));
}

if (expirationTokens == null)
{
throw new ArgumentNullException(nameof(expirationTokens));
}

CompilationResult = compilationResult;
ExpirationTokens = expirationTokens;
}

/// <summary>
/// Initializes a new instance of <see cref="CompilerCacheResult"/> for a failed file lookup.
/// </summary>
protected CompilerCacheResult()
public CompilerCacheResult(IList<IChangeToken> expirationTokens)
{
if (expirationTokens == null)
{
throw new ArgumentNullException(nameof(expirationTokens));
}

CompilationResult = null;
ExpirationTokens = expirationTokens;
}

/// <summary>
/// The <see cref="Compilation.CompilationResult"/>.
/// </summary>
/// <remarks>This property is null when file lookup failed.</remarks>
public CompilationResult CompilationResult { get; }

public IList<IChangeToken> ExpirationTokens { get; }

public bool IsFoundResult => CompilationResult != null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Mvc.Razor
/// Represents a <see cref="IRazorPageFactory"/> that creates <see cref="RazorPage"/> instances
/// from razor files in the file system.
/// </summary>
public class VirtualPathRazorPageFactory : IRazorPageFactory
public class DefaultRazorPageFactory : IRazorPageFactory
{
/// <remarks>
/// This delegate holds on to an instance of <see cref="IRazorCompilationService"/>.
Expand All @@ -24,7 +24,7 @@ public class VirtualPathRazorPageFactory : IRazorPageFactory
/// </summary>
/// <param name="razorCompilationService">The <see cref="IRazorCompilationService"/>.</param>
/// <param name="compilerCacheProvider">The <see cref="ICompilerCacheProvider"/>.</param>
public VirtualPathRazorPageFactory(
public DefaultRazorPageFactory(
IRazorCompilationService razorCompilationService,
ICompilerCacheProvider compilerCacheProvider)
{
Expand All @@ -46,7 +46,7 @@ private ICompilerCache CompilerCache
}

/// <inheritdoc />
public IRazorPage CreateInstance(string relativePath)
public RazorPageFactoryResult CreateFactory(string relativePath)
{
if (relativePath == null)
{
Expand All @@ -58,18 +58,20 @@ public IRazorPage CreateInstance(string relativePath)
// For tilde slash paths, drop the leading ~ to make it work with the underlying IFileProvider.
relativePath = relativePath.Substring(1);
}

var result = CompilerCache.GetOrAdd(relativePath, _compileDelegate);

if (result == CompilerCacheResult.FileNotFound)
if (result.IsFoundResult)
{
return null;
return new RazorPageFactoryResult(() =>
{
var page = (IRazorPage)Activator.CreateInstance(result.CompilationResult.CompiledType);
page.Path = relativePath;
return page;
}, result.ExpirationTokens);
}
else
{
return new RazorPageFactoryResult(result.ExpirationTokens);
}

var page = (IRazorPage)Activator.CreateInstance(result.CompilationResult.CompiledType);
page.Path = relativePath;

return page;
}
}
}
176 changes: 0 additions & 176 deletions src/Microsoft.AspNet.Mvc.Razor/DefaultViewLocationCache.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,6 @@ internal static void AddRazorViewEngineServices(IServiceCollection services)

services.TryAddSingleton<IRazorViewEngine, RazorViewEngine>();

// Caches view locations that are valid for the lifetime of the application.
services.TryAddSingleton<IViewLocationCache, DefaultViewLocationCache>();
services.TryAdd(ServiceDescriptor.Singleton<IChunkTreeCache>(serviceProvider =>
{
var cachedFileProvider = serviceProvider.GetRequiredService<IOptions<RazorViewEngineOptions>>();
Expand All @@ -148,9 +146,8 @@ internal static void AddRazorViewEngineServices(IServiceCollection services)
// In the default scenario the following services are singleton by virtue of being initialized as part of
// creating the singleton RazorViewEngine instance.
services.TryAddTransient<IRazorViewFactory, RazorViewFactory>();
services.TryAddTransient<IRazorPageFactory, VirtualPathRazorPageFactory>();
services.TryAddTransient<IRazorPageFactory, DefaultRazorPageFactory>();
services.TryAddTransient<IRazorCompilationService, RazorCompilationService>();
services.TryAddTransient<IViewStartProvider, ViewStartProvider>();
services.TryAddTransient<IMvcRazorHost, MvcRazorHost>();

// This caches Razor page activation details that are valid for the lifetime of the application.
Expand Down
6 changes: 3 additions & 3 deletions src/Microsoft.AspNet.Mvc.Razor/IRazorPageFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ namespace Microsoft.AspNet.Mvc.Razor
public interface IRazorPageFactory
{
/// <summary>
/// Creates a <see cref="IRazorPage"/> for the specified path.
/// Creates a <see cref="IRazorPage"/> factory for the specified path.
/// </summary>
/// <param name="relativePath">The path to locate the page.</param>
/// <returns>The IRazorPage instance if it exists, null otherwise.</returns>
IRazorPage CreateInstance(string relativePath);
/// <returns>The <see cref="RazorPageFactoryResult"/> instance.</returns>
RazorPageFactoryResult CreateFactory(string relativePath);
}
}
Loading

0 comments on commit 3891494

Please sign in to comment.