Avoid holding on to DependencyContext instance

Fixes #4527
This commit is contained in:
Pranav K 2016-04-26 15:40:18 -07:00
parent cba4d1dd0c
commit 0ff2f875d9
2 changed files with 77 additions and 66 deletions

View File

@ -29,12 +29,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
/// </summary> /// </summary>
public class DefaultRoslynCompilationService : ICompilationService public class DefaultRoslynCompilationService : ICompilationService
{ {
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IFileProvider _fileProvider; private readonly IFileProvider _fileProvider;
private readonly Action<RoslynCompilationContext> _compilationCallback; private readonly Action<RoslynCompilationContext> _compilationCallback;
private readonly CSharpParseOptions _parseOptions; private readonly CSharpParseOptions _parseOptions;
private readonly CSharpCompilationOptions _compilationOptions; private readonly CSharpCompilationOptions _compilationOptions;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly DependencyContext _dependencyContext;
private object _applicationReferencesLock = new object(); private object _applicationReferencesLock = new object();
private bool _applicationReferencesInitialized; private bool _applicationReferencesInitialized;
private List<MetadataReference> _applicationReferences; private List<MetadataReference> _applicationReferences;
@ -51,26 +51,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
IOptions<RazorViewEngineOptions> optionsAccessor, IOptions<RazorViewEngineOptions> optionsAccessor,
IRazorViewEngineFileProviderAccessor fileProviderAccessor, IRazorViewEngineFileProviderAccessor fileProviderAccessor,
ILoggerFactory loggerFactory) ILoggerFactory loggerFactory)
: this(
GetDependencyContext(environment),
optionsAccessor.Value,
fileProviderAccessor,
loggerFactory)
{ {
} _hostingEnvironment = environment;
// Internal for unit testing
internal DefaultRoslynCompilationService(
DependencyContext dependencyContext,
RazorViewEngineOptions viewEngineOptions,
IRazorViewEngineFileProviderAccessor fileProviderAccessor,
ILoggerFactory loggerFactory)
{
_dependencyContext = dependencyContext;
_fileProvider = fileProviderAccessor.FileProvider; _fileProvider = fileProviderAccessor.FileProvider;
_compilationCallback = viewEngineOptions.CompilationCallback; _compilationCallback = optionsAccessor.Value.CompilationCallback;
_parseOptions = viewEngineOptions.ParseOptions; _parseOptions = optionsAccessor.Value.ParseOptions;
_compilationOptions = viewEngineOptions.CompilationOptions; _compilationOptions = optionsAccessor.Value.CompilationOptions;
_logger = loggerFactory.CreateLogger<DefaultRoslynCompilationService>(); _logger = loggerFactory.CreateLogger<DefaultRoslynCompilationService>();
} }
@ -165,6 +151,22 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
} }
} }
/// <summary>
/// Gets the <see cref="DependencyContext"/>.
/// </summary>
/// <param name="hostingEnvironment">The <see cref="IHostingEnvironment"/>.</param>
/// <returns>The <see cref="DependencyContext"/>.</returns>
protected virtual DependencyContext GetDependencyContext(IHostingEnvironment hostingEnvironment)
{
if (hostingEnvironment.ApplicationName != null)
{
var applicationAssembly = Assembly.Load(new AssemblyName(hostingEnvironment.ApplicationName));
return DependencyContext.Load(applicationAssembly);
}
return null;
}
private Assembly LoadStream(MemoryStream assemblyStream, MemoryStream pdbStream) private Assembly LoadStream(MemoryStream assemblyStream, MemoryStream pdbStream)
{ {
#if NET451 #if NET451
@ -241,16 +243,17 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
private List<MetadataReference> GetApplicationReferences() private List<MetadataReference> GetApplicationReferences()
{ {
var metadataReferences = new List<MetadataReference>(); var metadataReferences = new List<MetadataReference>();
if (_dependencyContext == null) var dependencyContext = GetDependencyContext(_hostingEnvironment);
if (dependencyContext == null)
{ {
// Avoid null ref if the entry point does not have DependencyContext specified. // Avoid null ref if the entry point does not have DependencyContext specified.
return metadataReferences; return metadataReferences;
} }
var libraryPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase); var libraryPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
for (var i = 0; i < _dependencyContext.CompileLibraries.Count; i++) for (var i = 0; i < dependencyContext.CompileLibraries.Count; i++)
{ {
var library = _dependencyContext.CompileLibraries[i]; var library = dependencyContext.CompileLibraries[i];
IEnumerable<string> referencePaths; IEnumerable<string> referencePaths;
try try
{ {
@ -322,16 +325,5 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
mappedLineSpan.EndLinePosition.Line + 1, mappedLineSpan.EndLinePosition.Line + 1,
mappedLineSpan.EndLinePosition.Character + 1); mappedLineSpan.EndLinePosition.Character + 1);
} }
private static DependencyContext GetDependencyContext(IHostingEnvironment environment)
{
if (environment.ApplicationName != null)
{
var applicationAssembly = Assembly.Load(new AssemblyName(environment.ApplicationName));
return DependencyContext.Load(applicationAssembly);
}
return null;
}
} }
} }

View File

@ -4,12 +4,14 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Razor.Compilation; using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.DependencyModel; using Microsoft.Extensions.DependencyModel;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.Logging.Testing;
using Microsoft.Extensions.Options;
using Moq; using Moq;
using Xunit; using Xunit;
@ -24,11 +26,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var content = @" var content = @"
public class MyTestType {}"; public class MyTestType {}";
var compilationService = new DefaultRoslynCompilationService( var compilationService = new TestableRoslynCompilationService(
GetDependencyContext(), GetDependencyContext(),
GetOptions(), GetOptions(),
GetFileProviderAccessor(), GetFileProviderAccessor());
NullLoggerFactory.Instance);
var relativeFileInfo = new RelativeFileInfo( var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" }, new TestFileInfo { PhysicalPath = "SomePath" },
"some-relative-path"); "some-relative-path");
@ -52,11 +53,10 @@ this should fail";
var fileProvider = new TestFileProvider(); var fileProvider = new TestFileProvider();
var fileInfo = fileProvider.AddFile(viewPath, fileContent); var fileInfo = fileProvider.AddFile(viewPath, fileContent);
var compilationService = new DefaultRoslynCompilationService( var compilationService = new TestableRoslynCompilationService(
GetDependencyContext(), GetDependencyContext(),
GetOptions(), GetOptions(),
GetFileProviderAccessor(fileProvider), GetFileProviderAccessor(fileProvider));
NullLoggerFactory.Instance);
var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path"); var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path");
// Act // Act
@ -77,11 +77,10 @@ this should fail";
var fileContent = "file content"; var fileContent = "file content";
var content = @"this should fail"; var content = @"this should fail";
var compilationService = new DefaultRoslynCompilationService( var compilationService = new TestableRoslynCompilationService(
GetDependencyContext(), GetDependencyContext(),
GetOptions(), GetOptions(),
GetFileProviderAccessor(), GetFileProviderAccessor());
NullLoggerFactory.Instance);
var relativeFileInfo = new RelativeFileInfo( var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { Content = fileContent }, new TestFileInfo { Content = fileContent },
"some-relative-path"); "some-relative-path");
@ -113,11 +112,10 @@ this should fail";
var fileProvider = new TestFileProvider(); var fileProvider = new TestFileProvider();
fileProvider.AddFile(path, mockFileInfo.Object); fileProvider.AddFile(path, mockFileInfo.Object);
var compilationService = new DefaultRoslynCompilationService( var compilationService = new TestableRoslynCompilationService(
GetDependencyContext(), GetDependencyContext(),
GetOptions(), GetOptions(),
GetFileProviderAccessor(), GetFileProviderAccessor());
NullLoggerFactory.Instance);
var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, path); var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, path);
// Act // Act
@ -146,11 +144,10 @@ public class MyNonCustomDefinedClass {}
var options = GetOptions(); var options = GetOptions();
options.ParseOptions = options.ParseOptions.WithPreprocessorSymbols("MY_CUSTOM_DEFINE"); options.ParseOptions = options.ParseOptions.WithPreprocessorSymbols("MY_CUSTOM_DEFINE");
var compilationService = new DefaultRoslynCompilationService( var compilationService = new TestableRoslynCompilationService(
GetDependencyContext(), GetDependencyContext(),
options, options,
GetFileProviderAccessor(), GetFileProviderAccessor());
NullLoggerFactory.Instance);
var relativeFileInfo = new RelativeFileInfo( var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" }, new TestFileInfo { PhysicalPath = "SomePath" },
"some-relative-path"); "some-relative-path");
@ -174,11 +171,10 @@ public class MyNonCustomDefinedClass {}
var options = new RazorViewEngineOptions(); var options = new RazorViewEngineOptions();
options.FileProviders.Add(fileProvider); options.FileProviders.Add(fileProvider);
var compilationService = new DefaultRoslynCompilationService( var compilationService = new TestableRoslynCompilationService(
GetDependencyContext(), GetDependencyContext(),
options, options,
GetFileProviderAccessor(fileProvider), GetFileProviderAccessor(fileProvider));
NullLoggerFactory.Instance);
var assemblyName = "random-assembly-name"; var assemblyName = "random-assembly-name";
@ -261,11 +257,10 @@ public class MyNonCustomDefinedClass {}
var content = "public class MyTestType {}"; var content = "public class MyTestType {}";
RoslynCompilationContext usedCompilation = null; RoslynCompilationContext usedCompilation = null;
var compilationService = new DefaultRoslynCompilationService( var compilationService = new TestableRoslynCompilationService(
GetDependencyContext(), GetDependencyContext(),
GetOptions(callback: c => usedCompilation = c), GetOptions(callback: c => usedCompilation = c),
GetFileProviderAccessor(), GetFileProviderAccessor());
NullLoggerFactory.Instance);
var relativeFileInfo = new RelativeFileInfo( var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" }, new TestFileInfo { PhysicalPath = "SomePath" },
@ -283,11 +278,10 @@ public class MyNonCustomDefinedClass {}
{ {
// Arrange // Arrange
var content = "public class MyTestType {}"; var content = "public class MyTestType {}";
var compilationService = new DefaultRoslynCompilationService( var compilationService = new TestableRoslynCompilationService(
dependencyContext: null, dependencyContext: null,
viewEngineOptions: GetOptions(), viewEngineOptions: GetOptions(),
fileProviderAccessor: GetFileProviderAccessor(), fileProviderAccessor: GetFileProviderAccessor());
loggerFactory: NullLoggerFactory.Instance);
var relativeFileInfo = new RelativeFileInfo( var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" }, new TestFileInfo { PhysicalPath = "SomePath" },
@ -313,11 +307,10 @@ public class MyNonCustomDefinedClass {}
new CompilationLibrary[0], new CompilationLibrary[0],
new RuntimeLibrary[0], new RuntimeLibrary[0],
Enumerable.Empty<RuntimeFallbacks>()); Enumerable.Empty<RuntimeFallbacks>());
var compilationService = new DefaultRoslynCompilationService( var compilationService = new TestableRoslynCompilationService(
dependencyContext: dependencyContext, dependencyContext: dependencyContext,
viewEngineOptions: GetOptions(), viewEngineOptions: GetOptions(),
fileProviderAccessor: GetFileProviderAccessor(), fileProviderAccessor: GetFileProviderAccessor());
loggerFactory: NullLoggerFactory.Instance);
var relativeFileInfo = new RelativeFileInfo( var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" }, new TestFileInfo { PhysicalPath = "SomePath" },
@ -341,11 +334,10 @@ public class MyNonCustomDefinedClass {}
context.Compilation = context.Compilation.RemoveAllReferences(); context.Compilation = context.Compilation.RemoveAllReferences();
}); });
var content = "public class MyTestType {}"; var content = "public class MyTestType {}";
var compilationService = new DefaultRoslynCompilationService( var compilationService = new TestableRoslynCompilationService(
dependencyContext: GetDependencyContext(), dependencyContext: GetDependencyContext(),
viewEngineOptions: options, viewEngineOptions: options,
fileProviderAccessor: GetFileProviderAccessor(), fileProviderAccessor: GetFileProviderAccessor());
loggerFactory: NullLoggerFactory.Instance);
var relativeFileInfo = new RelativeFileInfo( var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" }, new TestFileInfo { PhysicalPath = "SomePath" },
@ -371,11 +363,10 @@ public class MyNonCustomDefinedClass {}
.AddReferences(MetadataReference.CreateFromFile(assemblyLocation)); .AddReferences(MetadataReference.CreateFromFile(assemblyLocation));
}); });
var content = "public class MyTestType {}"; var content = "public class MyTestType {}";
var compilationService = new DefaultRoslynCompilationService( var compilationService = new TestableRoslynCompilationService(
dependencyContext: null, dependencyContext: null,
viewEngineOptions: options, viewEngineOptions: options,
fileProviderAccessor: GetFileProviderAccessor(), fileProviderAccessor: GetFileProviderAccessor());
loggerFactory: NullLoggerFactory.Instance);
var relativeFileInfo = new RelativeFileInfo( var relativeFileInfo = new RelativeFileInfo(
new TestFileInfo { PhysicalPath = "SomePath" }, new TestFileInfo { PhysicalPath = "SomePath" },
@ -422,5 +413,33 @@ public class MyNonCustomDefinedClass {}
var assembly = typeof(DefaultRoslynCompilationServiceTest).GetTypeInfo().Assembly; var assembly = typeof(DefaultRoslynCompilationServiceTest).GetTypeInfo().Assembly;
return DependencyContext.Load(assembly); return DependencyContext.Load(assembly);
} }
private class TestableRoslynCompilationService : DefaultRoslynCompilationService
{
private readonly DependencyContext _dependencyContext;
public TestableRoslynCompilationService(
DependencyContext dependencyContext,
RazorViewEngineOptions viewEngineOptions,
IRazorViewEngineFileProviderAccessor fileProviderAccessor)
: base(
Mock.Of<IHostingEnvironment>(),
GetAccessor(viewEngineOptions),
fileProviderAccessor,
NullLoggerFactory.Instance)
{
_dependencyContext = dependencyContext;
}
private static IOptions<RazorViewEngineOptions> GetAccessor(RazorViewEngineOptions options)
{
var optionsAccessor = new Mock<IOptions<RazorViewEngineOptions>>();
optionsAccessor.SetupGet(a => a.Value).Returns(options);
return optionsAccessor.Object;
}
protected override DependencyContext GetDependencyContext(IHostingEnvironment hostingEnvironment)
=> _dependencyContext;
}
} }
} }