From 51c285574b02e381a0744828d40c2a81001a5a25 Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Wed, 1 Jun 2016 11:39:10 -0700 Subject: [PATCH] Add `AdditionalCompilationReferences` option to `RazorViewEngineOptions`. - `AdditionalCompilationReferences` is a purely additive option to add `MetadataReference` instances to be used for Razor compilation. - Added a test to validate the compilation references get combined with the `ApplicationPartManager`s metadata references. #4497 --- .../DefaultRoslynCompilationService.cs | 37 +++++++++++------ .../RazorViewEngineOptions.cs | 7 ++++ .../DefaultRoslynCompilationServiceTest.cs | 40 +++++++++++++++++++ 3 files changed, 72 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRoslynCompilationService.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRoslynCompilationService.cs index c759f3d92f..bc7701734f 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRoslynCompilationService.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRoslynCompilationService.cs @@ -32,10 +32,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal private readonly Action _compilationCallback; private readonly CSharpParseOptions _parseOptions; private readonly CSharpCompilationOptions _compilationOptions; + private readonly IList _additionalMetadataReferences; private readonly ILogger _logger; - private object _applicationReferencesLock = new object(); - private bool _applicationReferencesInitialized; - private IList _applicationReferences; + private object _compilationReferencesLock = new object(); + private bool _compilationReferencesInitialized; + private IList _compilationReferences; /// /// Initalizes a new instance of the class. @@ -55,18 +56,19 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal _compilationCallback = optionsAccessor.Value.CompilationCallback; _parseOptions = optionsAccessor.Value.ParseOptions; _compilationOptions = optionsAccessor.Value.CompilationOptions; + _additionalMetadataReferences = optionsAccessor.Value.AdditionalCompilationReferences; _logger = loggerFactory.CreateLogger(); } - private IList ApplicationReferences + private IList CompilationReferences { get { return LazyInitializer.EnsureInitialized( - ref _applicationReferences, - ref _applicationReferencesInitialized, - ref _applicationReferencesLock, - GetApplicationReferences); + ref _compilationReferences, + ref _compilationReferencesInitialized, + ref _compilationReferencesLock, + GetCompilationReferences); } } @@ -99,7 +101,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal assemblyName, options: _compilationOptions, syntaxTrees: new[] { syntaxTree }, - references: ApplicationReferences); + references: CompilationReferences); compilation = Rewrite(compilation); @@ -118,7 +120,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal if (!result.Success) { - if (!compilation.References.Any() && !ApplicationReferences.Any()) + if (!compilation.References.Any() && !CompilationReferences.Any()) { // DependencyModel had no references specified and the user did not use the // CompilationCallback to add extra references. It is likely that the user did not specify @@ -153,11 +155,22 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal /// Gets the sequence of instances used for compilation. /// /// The instances. - protected virtual IList GetApplicationReferences() + protected virtual IList GetCompilationReferences() { var feature = new MetadataReferenceFeature(); _partManager.PopulateFeature(feature); - return feature.MetadataReferences; + var applicationReferences = feature.MetadataReferences; + + if (_additionalMetadataReferences.Count == 0) + { + return applicationReferences; + } + + var compilationReferences = new List(applicationReferences.Count + _additionalMetadataReferences.Count); + compilationReferences.AddRange(applicationReferences); + compilationReferences.AddRange(_additionalMetadataReferences); + + return compilationReferences; } private Assembly LoadStream(MemoryStream assemblyStream, MemoryStream pdbStream) diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngineOptions.cs b/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngineOptions.cs index e279c88ec9..2654a93ee7 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngineOptions.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngineOptions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using Microsoft.AspNetCore.Mvc.Razor.Compilation; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.Extensions.FileProviders; @@ -90,6 +91,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor /// public IList AreaViewLocationFormats { get; } = new List(); + /// + /// Gets the instances that should be included in Razor compilation, along with + /// those discovered by s. + /// + public IList AdditionalCompilationReferences { get; } = new List(); + /// /// Gets or sets the callback that is used to customize Razor compilation /// to change compilation settings you can update property. diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRoslynCompilationServiceTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRoslynCompilationServiceTest.cs index cb223a9844..957ddced77 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRoslynCompilationServiceTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRoslynCompilationServiceTest.cs @@ -2,6 +2,8 @@ // 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.Linq; using System.Reflection; using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.Razor.Compilation; @@ -316,6 +318,34 @@ public class MyNonCustomDefinedClass {} Assert.NotNull(result.CompiledType); } + [Fact] + public void GetCompilationReferences_CombinesApplicationPartAndOptionMetadataReferences() + { + // Arrange + var options = new RazorViewEngineOptions(); + var objectAssemblyLocation = typeof(object).GetTypeInfo().Assembly.Location; + var objectAssemblyMetadataReference = MetadataReference.CreateFromFile(objectAssemblyLocation); + options.AdditionalCompilationReferences.Add(objectAssemblyMetadataReference); + var applicationPartManager = GetApplicationPartManager(); + var compilationService = new TestRoslynCompilationService(applicationPartManager, options); + var feature = new MetadataReferenceFeature(); + applicationPartManager.PopulateFeature(feature); + var partReferences = feature.MetadataReferences; + var expectedReferences = new List(); + expectedReferences.AddRange(partReferences); + expectedReferences.Add(objectAssemblyMetadataReference); + var expectedReferenceDisplays = expectedReferences.Select(reference => reference.Display); + + // Act + var references = compilationService.GetCompilationReferencesPublic(); + var referenceDisplays = references.Select(reference => reference.Display); + + // Assert + Assert.NotNull(references); + Assert.NotEmpty(references); + Assert.Equal(expectedReferenceDisplays, referenceDisplays); + } + private static DiagnosticDescriptor GetDiagnosticDescriptor(string messageFormat) { return new DiagnosticDescriptor( @@ -375,5 +405,15 @@ public class MyNonCustomDefinedClass {} GetFileProviderAccessor(fileProvider), NullLoggerFactory.Instance); } + + private class TestRoslynCompilationService : DefaultRoslynCompilationService + { + public TestRoslynCompilationService(ApplicationPartManager partManager, RazorViewEngineOptions options) + : base(partManager, GetAccessor(options), GetFileProviderAccessor(), NullLoggerFactory.Instance) + { + } + + public IList GetCompilationReferencesPublic() => GetCompilationReferences(); + } } }