* Avoid lazyily evaluating IRazorCompilationService in

VirtualPathRazorPageFactory
* Cleanup comments on the lifetime of Razor services.
This commit is contained in:
Pranav K 2015-08-20 11:39:57 -07:00
parent ddc74e5396
commit c0d4981452
3 changed files with 20 additions and 40 deletions

View File

@ -96,28 +96,22 @@ namespace Microsoft.Framework.DependencyInjection
return new DefaultChunkTreeCache(cachedFileProvider.Options.FileProvider); return new DefaultChunkTreeCache(cachedFileProvider.Options.FileProvider);
})); }));
// The host is designed to be discarded after consumption and is very inexpensive to initialize.
services.TryAddTransient<IMvcRazorHost, MvcRazorHost>();
// Caches compilation artifacts across the lifetime of the application. // Caches compilation artifacts across the lifetime of the application.
services.TryAddSingleton<ICompilerCacheProvider, DefaultCompilerCacheProvider>(); services.TryAddSingleton<ICompilerCacheProvider, DefaultCompilerCacheProvider>();
// This caches compilation related details that are valid across the lifetime of the application // This caches compilation related details that are valid across the lifetime of the application.
// and is required to be a singleton.
services.TryAddSingleton<ICompilationService, RoslynCompilationService>(); services.TryAddSingleton<ICompilationService, RoslynCompilationService>();
// Both the compiler cache and roslyn compilation service hold on the compilation related // In the default scenario the following services are singleton by virtue of being initialized as part of
// caches. RazorCompilation service is just an adapter service, and it is transient to ensure // creating the singleton RazorViewEngine instance.
// the IMvcRazorHost dependency does not maintain state.
services.TryAddTransient<IRazorCompilationService, RazorCompilationService>();
// The ViewStartProvider needs to be able to consume scoped instances of IRazorPageFactory
services.TryAddScoped<IViewStartProvider, ViewStartProvider>();
services.TryAddTransient<IRazorViewFactory, RazorViewFactory>(); services.TryAddTransient<IRazorViewFactory, RazorViewFactory>();
services.TryAddSingleton<IRazorPageActivator, RazorPageActivator>(); services.TryAddTransient<IRazorPageFactory, VirtualPathRazorPageFactory>();
services.TryAddTransient<IRazorCompilationService, RazorCompilationService>();
services.TryAddTransient<IViewStartProvider, ViewStartProvider>();
services.TryAddTransient<IMvcRazorHost, MvcRazorHost>();
// Virtual path view factory needs to stay scoped so views can get get scoped services. // This caches Razor page activation details that are valid for the lifetime of the application.
services.TryAddScoped<IRazorPageFactory, VirtualPathRazorPageFactory>(); services.TryAddSingleton<IRazorPageActivator, RazorPageActivator>();
// Only want one ITagHelperActivator so it can cache Type activation information. Types won't conflict. // Only want one ITagHelperActivator so it can cache Type activation information. Types won't conflict.
services.TryAddSingleton<ITagHelperActivator, DefaultTagHelperActivator>(); services.TryAddSingleton<ITagHelperActivator, DefaultTagHelperActivator>();

View File

@ -3,7 +3,6 @@
using System; using System;
using Microsoft.AspNet.Mvc.Razor.Compilation; using Microsoft.AspNet.Mvc.Razor.Compilation;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.Razor namespace Microsoft.AspNet.Mvc.Razor
@ -14,40 +13,26 @@ namespace Microsoft.AspNet.Mvc.Razor
/// </summary> /// </summary>
public class VirtualPathRazorPageFactory : IRazorPageFactory public class VirtualPathRazorPageFactory : IRazorPageFactory
{ {
private readonly IServiceProvider _serviceProvider; /// <remarks>
/// This delegate holds on to an instance of <see cref="IRazorCompilationService"/>.
/// </remarks>
private readonly Func<RelativeFileInfo, CompilationResult> _compileDelegate;
private readonly ICompilerCacheProvider _compilerCacheProvider; private readonly ICompilerCacheProvider _compilerCacheProvider;
private IRazorCompilationService _razorcompilationService;
private ICompilerCache _compilerCache; private ICompilerCache _compilerCache;
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="VirtualPathRazorPageFactory"/>. /// Initializes a new instance of <see cref="VirtualPathRazorPageFactory"/>.
/// </summary> /// </summary>
/// <param name="serviceProvider">The request specific <see cref="IServiceProvider"/>.</param> /// <param name="razorCompilationService">The <see cref="IRazorCompilationService"/>.</param>
/// <param name="compilerCacheProvider">The <see cref="ICompilerCacheProvider"/>.</param> /// <param name="compilerCacheProvider">The <see cref="ICompilerCacheProvider"/>.</param>
public VirtualPathRazorPageFactory( public VirtualPathRazorPageFactory(
IServiceProvider serviceProvider, IRazorCompilationService razorCompilationService,
ICompilerCacheProvider compilerCacheProvider) ICompilerCacheProvider compilerCacheProvider)
{ {
_serviceProvider = serviceProvider; _compileDelegate = razorCompilationService.Compile;
_compilerCacheProvider = compilerCacheProvider; _compilerCacheProvider = compilerCacheProvider;
} }
private IRazorCompilationService RazorCompilationService
{
get
{
if (_razorcompilationService == null)
{
// it is ok to use the cached service provider because both this, and the
// resolved service are in a lifetime of Scoped.
// We don't want to get it upfront because it will force Roslyn to load.
_razorcompilationService = _serviceProvider.GetRequiredService<IRazorCompilationService>();
}
return _razorcompilationService;
}
}
private ICompilerCache CompilerCache private ICompilerCache CompilerCache
{ {
get get
@ -70,9 +55,7 @@ namespace Microsoft.AspNet.Mvc.Razor
relativePath = relativePath.Substring(1); relativePath = relativePath.Substring(1);
} }
var result = CompilerCache.GetOrAdd( var result = CompilerCache.GetOrAdd(relativePath, _compileDelegate);
relativePath,
RazorCompilationService.Compile);
if (result == CompilerCacheResult.FileNotFound) if (result == CompilerCacheResult.FileNotFound)
{ {

View File

@ -9,6 +9,7 @@ using Microsoft.AspNet.Mvc.Internal;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation; using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Mvc.Razor; using Microsoft.AspNet.Mvc.Razor;
using Microsoft.Dnx.Compilation;
using Microsoft.Dnx.Runtime; using Microsoft.Dnx.Runtime;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
@ -232,6 +233,8 @@ namespace Microsoft.AspNet.Mvc
private static void AddDnxServices(IServiceCollection serviceCollection) private static void AddDnxServices(IServiceCollection serviceCollection)
{ {
serviceCollection.AddInstance(Mock.Of<ILibraryManager>()); serviceCollection.AddInstance(Mock.Of<ILibraryManager>());
serviceCollection.AddInstance(Mock.Of<ILibraryExporter>());
serviceCollection.AddInstance(Mock.Of<ICompilerOptionsProvider>());
serviceCollection.AddInstance(Mock.Of<IAssemblyLoadContextAccessor>()); serviceCollection.AddInstance(Mock.Of<IAssemblyLoadContextAccessor>());
var applicationEnvironment = new Mock<IApplicationEnvironment>(); var applicationEnvironment = new Mock<IApplicationEnvironment>();