Move caching of compilation results to its own layer.
This will allow only creating the razor compilation when really needed, with the right lifetime.
This commit is contained in:
parent
13ee27c92c
commit
75084ba0cd
|
|
@ -9,13 +9,13 @@ using System.Reflection;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
public class CompilerCache
|
public class CompilerCache : ICompilerCache
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<string, CompilerCacheEntry> _cache;
|
private readonly ConcurrentDictionary<string, CompilerCacheEntry> _cache;
|
||||||
private static readonly Type[] EmptyType = new Type[0];
|
private static readonly Type[] EmptyType = new Type[0];
|
||||||
|
|
||||||
public CompilerCache([NotNull] IEnumerable<Assembly> assemblies)
|
public CompilerCache([NotNull] IAssemblyProvider provider)
|
||||||
: this(GetFileInfos(assemblies))
|
: this(GetFileInfos(provider.CandidateAssemblies))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ using System;
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An entry in <see cref="CompilerCache"/> that contain metadata about precompiled and dynamically compiled file.
|
/// An entry in <see cref="ICompilerCache"/> that contain metadata about precompiled and dynamically compiled file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CompilerCacheEntry
|
public class CompilerCacheEntry
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// 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.FileSystems;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
{
|
||||||
|
public interface ICompilerCache
|
||||||
|
{
|
||||||
|
CompilationResult GetOrAdd([NotNull] RelativeFileInfo fileInfo,
|
||||||
|
bool enableInstrumentation,
|
||||||
|
[NotNull] Func<CompilationResult> compile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,7 @@ using System;
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the result of compilation that does not come from the <see cref="CompilerCache" />.
|
/// Represents the result of compilation that does not come from the <see cref="ICompilerCache" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UncachedCompilationResult : CompilationResult
|
public class UncachedCompilationResult : CompilationResult
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -16,42 +16,18 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class RazorCompilationService : IRazorCompilationService
|
public class RazorCompilationService : IRazorCompilationService
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly ICompilationService _compilationService;
|
||||||
|
|
||||||
private readonly CompilerCache _cache;
|
|
||||||
private ICompilationService _compilationService;
|
|
||||||
|
|
||||||
private ICompilationService CompilationService
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_compilationService == null)
|
|
||||||
{
|
|
||||||
_compilationService = _serviceProvider.GetService<ICompilationService>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _compilationService;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly IMvcRazorHost _razorHost;
|
private readonly IMvcRazorHost _razorHost;
|
||||||
|
|
||||||
public RazorCompilationService(IServiceProvider serviceProvider,
|
public RazorCompilationService(ICompilationService compilationService,
|
||||||
IAssemblyProvider _assemblyProvider,
|
|
||||||
IMvcRazorHost razorHost)
|
IMvcRazorHost razorHost)
|
||||||
{
|
{
|
||||||
_serviceProvider = serviceProvider;
|
_compilationService = compilationService;
|
||||||
_razorHost = razorHost;
|
_razorHost = razorHost;
|
||||||
_cache = new CompilerCache(_assemblyProvider.CandidateAssemblies);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public CompilationResult Compile([NotNull] RelativeFileInfo file, bool isInstrumented)
|
public CompilationResult Compile([NotNull] RelativeFileInfo file, bool isInstrumented)
|
||||||
{
|
|
||||||
return _cache.GetOrAdd(file, isInstrumented, () => CompileCore(file, isInstrumented));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal CompilationResult CompileCore(RelativeFileInfo file, bool isInstrumented)
|
|
||||||
{
|
{
|
||||||
_razorHost.EnableInstrumentation = isInstrumented;
|
_razorHost.EnableInstrumentation = isInstrumented;
|
||||||
|
|
||||||
|
|
@ -68,7 +44,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
return CompilationResult.Failed(file.FileInfo, results.GeneratedCode, messages);
|
return CompilationResult.Failed(file.FileInfo, results.GeneratedCode, messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompilationService.Compile(file.FileInfo, results.GeneratedCode);
|
return _compilationService.Compile(file.FileInfo, results.GeneratedCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,30 +12,36 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class VirtualPathRazorPageFactory : IRazorPageFactory
|
public class VirtualPathRazorPageFactory : IRazorPageFactory
|
||||||
{
|
{
|
||||||
private IRazorCompilationService _compilationService;
|
|
||||||
private IRazorCompilationService CompilationService
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_compilationService == null)
|
|
||||||
{
|
|
||||||
_compilationService = _serviceProvider.GetService<IRazorCompilationService>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _compilationService;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly ITypeActivator _activator;
|
private readonly ITypeActivator _activator;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly IFileInfoCache _fileInfoCache;
|
private readonly IFileInfoCache _fileInfoCache;
|
||||||
|
private readonly ICompilerCache _compilerCache;
|
||||||
|
|
||||||
|
private IRazorCompilationService _razorcompilationService;
|
||||||
|
|
||||||
|
private IRazorCompilationService RazorCompilationService
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_razorcompilationService == null)
|
||||||
|
{
|
||||||
|
// it is ok to use the cached service provider because this service has
|
||||||
|
// a lifetime of Scoped.
|
||||||
|
_razorcompilationService = _serviceProvider.GetService<IRazorCompilationService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _razorcompilationService;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public VirtualPathRazorPageFactory(ITypeActivator typeActivator,
|
public VirtualPathRazorPageFactory(ITypeActivator typeActivator,
|
||||||
IServiceProvider serviceProvider,
|
IServiceProvider serviceProvider,
|
||||||
|
ICompilerCache compilerCache,
|
||||||
IFileInfoCache fileInfoCache)
|
IFileInfoCache fileInfoCache)
|
||||||
{
|
{
|
||||||
_activator = typeActivator;
|
_activator = typeActivator;
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
|
_compilerCache = compilerCache;
|
||||||
_fileInfoCache = fileInfoCache;
|
_fileInfoCache = fileInfoCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,7 +64,9 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
RelativePath = relativePath,
|
RelativePath = relativePath,
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = CompilationService.Compile(relativeFileInfo, enableInstrumentation);
|
var result = _compilerCache.GetOrAdd(relativeFileInfo, enableInstrumentation, () =>
|
||||||
|
RazorCompilationService.Compile(relativeFileInfo, enableInstrumentation));
|
||||||
|
|
||||||
var page = (IRazorPage)_activator.CreateInstance(_serviceProvider, result.CompiledType);
|
var page = (IRazorPage)_activator.CreateInstance(_serviceProvider, result.CompiledType);
|
||||||
page.Path = relativePath;
|
page.Path = relativePath;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
// The host is designed to be discarded after consumption and is very inexpensive to initialize.
|
// The host is designed to be discarded after consumption and is very inexpensive to initialize.
|
||||||
yield return describe.Transient<IMvcRazorHost, MvcRazorHost>();
|
yield return describe.Transient<IMvcRazorHost, MvcRazorHost>();
|
||||||
|
|
||||||
|
yield return describe.Singleton<ICompilerCache, CompilerCache>();
|
||||||
yield return describe.Singleton<ICompilationService, RoslynCompilationService>();
|
yield return describe.Singleton<ICompilationService, RoslynCompilationService>();
|
||||||
yield return describe.Singleton<IRazorCompilationService, RazorCompilationService>();
|
yield return describe.Singleton<IRazorCompilationService, RazorCompilationService>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(@"src\work\myapp", @"src\work\myapp\views\index\home.cshtml")]
|
[InlineData(@"src\work\myapp", @"src\work\myapp\views\index\home.cshtml")]
|
||||||
[InlineData(@"src\work\myapp\", @"src\work\myapp\views\index\home.cshtml")]
|
[InlineData(@"src\work\myapp\", @"src\work\myapp\views\index\home.cshtml")]
|
||||||
public void CompileCoreCalculatesRootRelativePath(string appPath, string viewPath)
|
public void CompileCalculatesRootRelativePath(string appPath, string viewPath)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var host = new Mock<IMvcRazorHost>();
|
var host = new Mock<IMvcRazorHost>();
|
||||||
|
|
@ -27,11 +27,6 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
.Returns(GetGeneratorResult())
|
.Returns(GetGeneratorResult())
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
|
|
||||||
var ap = new Mock<IAssemblyProvider>();
|
|
||||||
ap.SetupGet(e => e.CandidateAssemblies)
|
|
||||||
.Returns(Enumerable.Empty<Assembly>())
|
|
||||||
.Verifiable();
|
|
||||||
|
|
||||||
var fileInfo = new Mock<IFileInfo>();
|
var fileInfo = new Mock<IFileInfo>();
|
||||||
fileInfo.Setup(f => f.PhysicalPath).Returns(viewPath);
|
fileInfo.Setup(f => f.PhysicalPath).Returns(viewPath);
|
||||||
fileInfo.Setup(f => f.CreateReadStream()).Returns(Stream.Null);
|
fileInfo.Setup(f => f.CreateReadStream()).Returns(Stream.Null);
|
||||||
|
|
@ -40,11 +35,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
compiler.Setup(c => c.Compile(fileInfo.Object, It.IsAny<string>()))
|
compiler.Setup(c => c.Compile(fileInfo.Object, It.IsAny<string>()))
|
||||||
.Returns(CompilationResult.Successful(typeof(RazorCompilationServiceTest)));
|
.Returns(CompilationResult.Successful(typeof(RazorCompilationServiceTest)));
|
||||||
|
|
||||||
var serviceProvider = new Mock<IServiceProvider>();
|
var razorService = new RazorCompilationService(compiler.Object, host.Object);
|
||||||
serviceProvider.Setup(sp => sp.GetService(It.Is<Type>(t => t == typeof(ICompilationService))))
|
|
||||||
.Returns(compiler.Object);
|
|
||||||
|
|
||||||
var razorService = new RazorCompilationService(serviceProvider.Object, ap.Object, host.Object);
|
|
||||||
|
|
||||||
var relativeFileInfo = new RelativeFileInfo()
|
var relativeFileInfo = new RelativeFileInfo()
|
||||||
{
|
{
|
||||||
|
|
@ -53,7 +44,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
razorService.CompileCore(relativeFileInfo, isInstrumented: false);
|
razorService.Compile(relativeFileInfo, isInstrumented: false);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
host.Verify();
|
host.Verify();
|
||||||
|
|
@ -62,25 +53,20 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(false)]
|
[InlineData(false)]
|
||||||
[InlineData(true)]
|
[InlineData(true)]
|
||||||
public void CompileCoreSetsEnableInstrumentationOnHost(bool enableInstrumentation)
|
public void CompileSetsEnableInstrumentationOnHost(bool enableInstrumentation)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var host = new Mock<IMvcRazorHost>();
|
var host = new Mock<IMvcRazorHost>();
|
||||||
host.SetupAllProperties();
|
host.SetupAllProperties();
|
||||||
host.Setup(h => h.GenerateCode(It.IsAny<string>(), It.IsAny<Stream>()))
|
host.Setup(h => h.GenerateCode(It.IsAny<string>(), It.IsAny<Stream>()))
|
||||||
.Returns(GetGeneratorResult()); var assemblyProvider = new Mock<IAssemblyProvider>();
|
.Returns(GetGeneratorResult());
|
||||||
assemblyProvider.SetupGet(e => e.CandidateAssemblies)
|
|
||||||
.Returns(Enumerable.Empty<Assembly>());
|
|
||||||
|
|
||||||
var compiler = new Mock<ICompilationService>();
|
var compiler = new Mock<ICompilationService>();
|
||||||
compiler.Setup(c => c.Compile(It.IsAny<IFileInfo>(), It.IsAny<string>()))
|
compiler.Setup(c => c.Compile(It.IsAny<IFileInfo>(), It.IsAny<string>()))
|
||||||
.Returns(CompilationResult.Successful(GetType()));
|
.Returns(CompilationResult.Successful(GetType()));
|
||||||
|
|
||||||
var serviceProvider = new Mock<IServiceProvider>();
|
var razorService = new RazorCompilationService(compiler.Object, host.Object);
|
||||||
serviceProvider.Setup(sp => sp.GetService(It.Is<Type>(t => t == typeof(ICompilationService))))
|
|
||||||
.Returns(compiler.Object);
|
|
||||||
|
|
||||||
var razorService = new RazorCompilationService(serviceProvider.Object, assemblyProvider.Object, host.Object);
|
|
||||||
var relativeFileInfo = new RelativeFileInfo()
|
var relativeFileInfo = new RelativeFileInfo()
|
||||||
{
|
{
|
||||||
FileInfo = Mock.Of<IFileInfo>(),
|
FileInfo = Mock.Of<IFileInfo>(),
|
||||||
|
|
@ -88,7 +74,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
razorService.CompileCore(relativeFileInfo, isInstrumented: enableInstrumentation);
|
razorService.Compile(relativeFileInfo, isInstrumented: enableInstrumentation);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(enableInstrumentation, host.Object.EnableInstrumentation);
|
Assert.Equal(enableInstrumentation, host.Object.EnableInstrumentation);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue