diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRoslynCompilationService.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRoslynCompilationService.cs index d5a92b5b7c..b886d11b41 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRoslynCompilationService.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRoslynCompilationService.cs @@ -29,12 +29,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal /// public class DefaultRoslynCompilationService : ICompilationService { + private readonly IHostingEnvironment _hostingEnvironment; private readonly IFileProvider _fileProvider; private readonly Action _compilationCallback; private readonly CSharpParseOptions _parseOptions; private readonly CSharpCompilationOptions _compilationOptions; private readonly ILogger _logger; - private readonly DependencyContext _dependencyContext; private object _applicationReferencesLock = new object(); private bool _applicationReferencesInitialized; private List _applicationReferences; @@ -51,26 +51,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal IOptions optionsAccessor, IRazorViewEngineFileProviderAccessor fileProviderAccessor, ILoggerFactory loggerFactory) - : this( - GetDependencyContext(environment), - optionsAccessor.Value, - fileProviderAccessor, - loggerFactory) { - } - - // Internal for unit testing - internal DefaultRoslynCompilationService( - DependencyContext dependencyContext, - RazorViewEngineOptions viewEngineOptions, - IRazorViewEngineFileProviderAccessor fileProviderAccessor, - ILoggerFactory loggerFactory) - { - _dependencyContext = dependencyContext; + _hostingEnvironment = environment; _fileProvider = fileProviderAccessor.FileProvider; - _compilationCallback = viewEngineOptions.CompilationCallback; - _parseOptions = viewEngineOptions.ParseOptions; - _compilationOptions = viewEngineOptions.CompilationOptions; + _compilationCallback = optionsAccessor.Value.CompilationCallback; + _parseOptions = optionsAccessor.Value.ParseOptions; + _compilationOptions = optionsAccessor.Value.CompilationOptions; _logger = loggerFactory.CreateLogger(); } @@ -165,6 +151,22 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal } } + /// + /// Gets the . + /// + /// The . + /// The . + 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) { #if NET451 @@ -241,16 +243,17 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal private List GetApplicationReferences() { var metadataReferences = new List(); - if (_dependencyContext == null) + var dependencyContext = GetDependencyContext(_hostingEnvironment); + if (dependencyContext == null) { // Avoid null ref if the entry point does not have DependencyContext specified. return metadataReferences; } var libraryPaths = new HashSet(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 referencePaths; try { @@ -322,16 +325,5 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal mappedLineSpan.EndLinePosition.Line + 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; - } } } diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRoslynCompilationServiceTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRoslynCompilationServiceTest.cs index ca68fa2aac..41abdb501e 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRoslynCompilationServiceTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRoslynCompilationServiceTest.cs @@ -4,12 +4,14 @@ using System; using System.Linq; using System.Reflection; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Razor.Compilation; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; using Microsoft.Extensions.DependencyModel; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging.Testing; +using Microsoft.Extensions.Options; using Moq; using Xunit; @@ -24,11 +26,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal var content = @" public class MyTestType {}"; - var compilationService = new DefaultRoslynCompilationService( + var compilationService = new TestableRoslynCompilationService( GetDependencyContext(), GetOptions(), - GetFileProviderAccessor(), - NullLoggerFactory.Instance); + GetFileProviderAccessor()); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path"); @@ -52,11 +53,10 @@ this should fail"; var fileProvider = new TestFileProvider(); var fileInfo = fileProvider.AddFile(viewPath, fileContent); - var compilationService = new DefaultRoslynCompilationService( + var compilationService = new TestableRoslynCompilationService( GetDependencyContext(), GetOptions(), - GetFileProviderAccessor(fileProvider), - NullLoggerFactory.Instance); + GetFileProviderAccessor(fileProvider)); var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path"); // Act @@ -77,11 +77,10 @@ this should fail"; var fileContent = "file content"; var content = @"this should fail"; - var compilationService = new DefaultRoslynCompilationService( + var compilationService = new TestableRoslynCompilationService( GetDependencyContext(), GetOptions(), - GetFileProviderAccessor(), - NullLoggerFactory.Instance); + GetFileProviderAccessor()); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { Content = fileContent }, "some-relative-path"); @@ -113,11 +112,10 @@ this should fail"; var fileProvider = new TestFileProvider(); fileProvider.AddFile(path, mockFileInfo.Object); - var compilationService = new DefaultRoslynCompilationService( + var compilationService = new TestableRoslynCompilationService( GetDependencyContext(), GetOptions(), - GetFileProviderAccessor(), - NullLoggerFactory.Instance); + GetFileProviderAccessor()); var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, path); // Act @@ -146,11 +144,10 @@ public class MyNonCustomDefinedClass {} var options = GetOptions(); options.ParseOptions = options.ParseOptions.WithPreprocessorSymbols("MY_CUSTOM_DEFINE"); - var compilationService = new DefaultRoslynCompilationService( + var compilationService = new TestableRoslynCompilationService( GetDependencyContext(), options, - GetFileProviderAccessor(), - NullLoggerFactory.Instance); + GetFileProviderAccessor()); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, "some-relative-path"); @@ -174,11 +171,10 @@ public class MyNonCustomDefinedClass {} var options = new RazorViewEngineOptions(); options.FileProviders.Add(fileProvider); - var compilationService = new DefaultRoslynCompilationService( + var compilationService = new TestableRoslynCompilationService( GetDependencyContext(), options, - GetFileProviderAccessor(fileProvider), - NullLoggerFactory.Instance); + GetFileProviderAccessor(fileProvider)); var assemblyName = "random-assembly-name"; @@ -261,11 +257,10 @@ public class MyNonCustomDefinedClass {} var content = "public class MyTestType {}"; RoslynCompilationContext usedCompilation = null; - var compilationService = new DefaultRoslynCompilationService( + var compilationService = new TestableRoslynCompilationService( GetDependencyContext(), GetOptions(callback: c => usedCompilation = c), - GetFileProviderAccessor(), - NullLoggerFactory.Instance); + GetFileProviderAccessor()); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, @@ -283,11 +278,10 @@ public class MyNonCustomDefinedClass {} { // Arrange var content = "public class MyTestType {}"; - var compilationService = new DefaultRoslynCompilationService( + var compilationService = new TestableRoslynCompilationService( dependencyContext: null, viewEngineOptions: GetOptions(), - fileProviderAccessor: GetFileProviderAccessor(), - loggerFactory: NullLoggerFactory.Instance); + fileProviderAccessor: GetFileProviderAccessor()); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, @@ -313,11 +307,10 @@ public class MyNonCustomDefinedClass {} new CompilationLibrary[0], new RuntimeLibrary[0], Enumerable.Empty()); - var compilationService = new DefaultRoslynCompilationService( + var compilationService = new TestableRoslynCompilationService( dependencyContext: dependencyContext, viewEngineOptions: GetOptions(), - fileProviderAccessor: GetFileProviderAccessor(), - loggerFactory: NullLoggerFactory.Instance); + fileProviderAccessor: GetFileProviderAccessor()); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, @@ -341,11 +334,10 @@ public class MyNonCustomDefinedClass {} context.Compilation = context.Compilation.RemoveAllReferences(); }); var content = "public class MyTestType {}"; - var compilationService = new DefaultRoslynCompilationService( + var compilationService = new TestableRoslynCompilationService( dependencyContext: GetDependencyContext(), viewEngineOptions: options, - fileProviderAccessor: GetFileProviderAccessor(), - loggerFactory: NullLoggerFactory.Instance); + fileProviderAccessor: GetFileProviderAccessor()); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, @@ -371,11 +363,10 @@ public class MyNonCustomDefinedClass {} .AddReferences(MetadataReference.CreateFromFile(assemblyLocation)); }); var content = "public class MyTestType {}"; - var compilationService = new DefaultRoslynCompilationService( + var compilationService = new TestableRoslynCompilationService( dependencyContext: null, viewEngineOptions: options, - fileProviderAccessor: GetFileProviderAccessor(), - loggerFactory: NullLoggerFactory.Instance); + fileProviderAccessor: GetFileProviderAccessor()); var relativeFileInfo = new RelativeFileInfo( new TestFileInfo { PhysicalPath = "SomePath" }, @@ -422,5 +413,33 @@ public class MyNonCustomDefinedClass {} var assembly = typeof(DefaultRoslynCompilationServiceTest).GetTypeInfo().Assembly; return DependencyContext.Load(assembly); } + + private class TestableRoslynCompilationService : DefaultRoslynCompilationService + { + private readonly DependencyContext _dependencyContext; + + public TestableRoslynCompilationService( + DependencyContext dependencyContext, + RazorViewEngineOptions viewEngineOptions, + IRazorViewEngineFileProviderAccessor fileProviderAccessor) + : base( + Mock.Of(), + GetAccessor(viewEngineOptions), + fileProviderAccessor, + NullLoggerFactory.Instance) + { + _dependencyContext = dependencyContext; + } + + private static IOptions GetAccessor(RazorViewEngineOptions options) + { + var optionsAccessor = new Mock>(); + optionsAccessor.SetupGet(a => a.Value).Returns(options); + return optionsAccessor.Object; + } + + protected override DependencyContext GetDependencyContext(IHostingEnvironment hostingEnvironment) + => _dependencyContext; + } } }