diff --git a/src/Microsoft.AspNet.Mvc.Razor/Precompilation/RazorPreCompileModule.cs b/src/Microsoft.AspNet.Mvc.Razor/Precompilation/RazorPreCompileModule.cs index 2906134886..fb1d7b6ba6 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Precompilation/RazorPreCompileModule.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Precompilation/RazorPreCompileModule.cs @@ -2,11 +2,14 @@ // 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 System.Runtime.Versioning; using Microsoft.AspNet.FileProviders; using Microsoft.Dnx.Compilation.CSharp; using Microsoft.Dnx.Runtime; using Microsoft.Framework.Caching.Memory; using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.Internal; namespace Microsoft.AspNet.Mvc.Razor.Precompilation { @@ -16,7 +19,9 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation public abstract class RazorPreCompileModule : ICompileModule { private readonly IAssemblyLoadContext _loadContext; - private readonly IMemoryCache _memoryCache; + private readonly object _memoryCacheLookupLock = new object(); + private readonly Dictionary _memoryCacheLookup = + new Dictionary(); /// /// Instantiates a new instance. @@ -25,11 +30,6 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation public RazorPreCompileModule(IServiceProvider services) { _loadContext = services.GetRequiredService(); - - // When CompactOnMemoryPressure is true, the MemoryCache evicts items at every gen2 collection. - // In DTH, gen2 happens frequently enough to make it undesirable for caching precompilation results. We'll - // disable listening for memory pressure for the MemoryCache instance used by precompilation. - _memoryCache = new MemoryCache(new MemoryCacheOptions { CompactOnMemoryPressure = false }); } /// @@ -43,11 +43,31 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation { var fileProvider = new PhysicalFileProvider(context.ProjectContext.ProjectDirectory); + MemoryCache memoryCache; + lock (_memoryCacheLookupLock) + { + var cacheKey = new PrecompilationCacheKey + { + Configuration = context.ProjectContext.Configuration, + TargetFramework = context.ProjectContext.TargetFramework + }; + + if (!_memoryCacheLookup.TryGetValue(cacheKey, out memoryCache)) + { + // When CompactOnMemoryPressure is true, the MemoryCache evicts items at every gen2 collection. + // In DTH, gen2 happens frequently enough to make it undesirable for caching precompilation results. We'll + // disable listening for memory pressure for the MemoryCache instance used by precompilation. + memoryCache = new MemoryCache(new MemoryCacheOptions { CompactOnMemoryPressure = false }); + + _memoryCacheLookup[cacheKey] = memoryCache; + } + } + var viewCompiler = new RazorPreCompiler( context, _loadContext, fileProvider, - _memoryCache) + memoryCache) { GenerateSymbols = GenerateSymbols }; @@ -59,5 +79,27 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation public void AfterCompile(AfterCompileContext context) { } + + private class PrecompilationCacheKey : IEquatable + { + public string Configuration { get; set; } + + public FrameworkName TargetFramework { get; set; } + + public bool Equals(PrecompilationCacheKey other) + { + return + other.TargetFramework == TargetFramework && + string.Equals(other.Configuration, Configuration, StringComparison.Ordinal); + } + + public override int GetHashCode() + { + return HashCodeCombiner + .Start() + .Add(Configuration) + .Add(TargetFramework); + } + } } } diff --git a/src/Microsoft.AspNet.Mvc.Razor/project.json b/src/Microsoft.AspNet.Mvc.Razor/project.json index e798e0c7ca..b770ca4889 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/project.json +++ b/src/Microsoft.AspNet.Mvc.Razor/project.json @@ -12,6 +12,7 @@ "Microsoft.AspNet.Mvc.Razor.Host": "6.0.0-*", "Microsoft.AspNet.Mvc.ViewFeatures": "6.0.0-*", "Microsoft.AspNet.PageExecutionInstrumentation.Interfaces": "1.0.0-*", + "Microsoft.Framework.HashCodeCombiner.Sources": { "version": "1.0.0-*", "type": "build" }, "Microsoft.Framework.NotNullAttribute.Sources": { "version": "1.0.0-*", "type": "build" }, "Microsoft.Framework.PropertyActivator.Sources": { "version": "1.0.0-*", "type": "build" }, "Microsoft.Framework.PropertyHelper.Sources": { "version": "1.0.0-*", "type": "build" },