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
This commit is contained in:
N. Taylor Mullen 2016-06-01 11:39:10 -07:00
parent 659cdc3364
commit 51c285574b
3 changed files with 72 additions and 12 deletions

View File

@ -32,10 +32,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
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 IList<MetadataReference> _additionalMetadataReferences;
private readonly ILogger _logger; private readonly ILogger _logger;
private object _applicationReferencesLock = new object(); private object _compilationReferencesLock = new object();
private bool _applicationReferencesInitialized; private bool _compilationReferencesInitialized;
private IList<MetadataReference> _applicationReferences; private IList<MetadataReference> _compilationReferences;
/// <summary> /// <summary>
/// Initalizes a new instance of the <see cref="DefaultRoslynCompilationService"/> class. /// Initalizes a new instance of the <see cref="DefaultRoslynCompilationService"/> class.
@ -55,18 +56,19 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
_compilationCallback = optionsAccessor.Value.CompilationCallback; _compilationCallback = optionsAccessor.Value.CompilationCallback;
_parseOptions = optionsAccessor.Value.ParseOptions; _parseOptions = optionsAccessor.Value.ParseOptions;
_compilationOptions = optionsAccessor.Value.CompilationOptions; _compilationOptions = optionsAccessor.Value.CompilationOptions;
_additionalMetadataReferences = optionsAccessor.Value.AdditionalCompilationReferences;
_logger = loggerFactory.CreateLogger<DefaultRoslynCompilationService>(); _logger = loggerFactory.CreateLogger<DefaultRoslynCompilationService>();
} }
private IList<MetadataReference> ApplicationReferences private IList<MetadataReference> CompilationReferences
{ {
get get
{ {
return LazyInitializer.EnsureInitialized( return LazyInitializer.EnsureInitialized(
ref _applicationReferences, ref _compilationReferences,
ref _applicationReferencesInitialized, ref _compilationReferencesInitialized,
ref _applicationReferencesLock, ref _compilationReferencesLock,
GetApplicationReferences); GetCompilationReferences);
} }
} }
@ -99,7 +101,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
assemblyName, assemblyName,
options: _compilationOptions, options: _compilationOptions,
syntaxTrees: new[] { syntaxTree }, syntaxTrees: new[] { syntaxTree },
references: ApplicationReferences); references: CompilationReferences);
compilation = Rewrite(compilation); compilation = Rewrite(compilation);
@ -118,7 +120,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
if (!result.Success) 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 // 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 // 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 <see cref="MetadataReference"/> instances used for compilation. /// Gets the sequence of <see cref="MetadataReference"/> instances used for compilation.
/// </summary> /// </summary>
/// <returns>The <see cref="MetadataReference"/> instances.</returns> /// <returns>The <see cref="MetadataReference"/> instances.</returns>
protected virtual IList<MetadataReference> GetApplicationReferences() protected virtual IList<MetadataReference> GetCompilationReferences()
{ {
var feature = new MetadataReferenceFeature(); var feature = new MetadataReferenceFeature();
_partManager.PopulateFeature(feature); _partManager.PopulateFeature(feature);
return feature.MetadataReferences; var applicationReferences = feature.MetadataReferences;
if (_additionalMetadataReferences.Count == 0)
{
return applicationReferences;
}
var compilationReferences = new List<MetadataReference>(applicationReferences.Count + _additionalMetadataReferences.Count);
compilationReferences.AddRange(applicationReferences);
compilationReferences.AddRange(_additionalMetadataReferences);
return compilationReferences;
} }
private Assembly LoadStream(MemoryStream assemblyStream, MemoryStream pdbStream) private Assembly LoadStream(MemoryStream assemblyStream, MemoryStream pdbStream)

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Razor.Compilation; using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
@ -90,6 +91,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor
/// </remarks> /// </remarks>
public IList<string> AreaViewLocationFormats { get; } = new List<string>(); public IList<string> AreaViewLocationFormats { get; } = new List<string>();
/// <summary>
/// Gets the <see cref="MetadataReference" /> instances that should be included in Razor compilation, along with
/// those discovered by <see cref="MetadataReferenceFeatureProvider" />s.
/// </summary>
public IList<MetadataReference> AdditionalCompilationReferences { get; } = new List<MetadataReference>();
/// <summary> /// <summary>
/// Gets or sets the callback that is used to customize Razor compilation /// Gets or sets the callback that is used to customize Razor compilation
/// to change compilation settings you can update <see cref="RoslynCompilationContext.Compilation"/> property. /// to change compilation settings you can update <see cref="RoslynCompilationContext.Compilation"/> property.

View File

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Razor.Compilation; using Microsoft.AspNetCore.Mvc.Razor.Compilation;
@ -316,6 +318,34 @@ public class MyNonCustomDefinedClass {}
Assert.NotNull(result.CompiledType); 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<MetadataReference>();
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) private static DiagnosticDescriptor GetDiagnosticDescriptor(string messageFormat)
{ {
return new DiagnosticDescriptor( return new DiagnosticDescriptor(
@ -375,5 +405,15 @@ public class MyNonCustomDefinedClass {}
GetFileProviderAccessor(fileProvider), GetFileProviderAccessor(fileProvider),
NullLoggerFactory.Instance); NullLoggerFactory.Instance);
} }
private class TestRoslynCompilationService : DefaultRoslynCompilationService
{
public TestRoslynCompilationService(ApplicationPartManager partManager, RazorViewEngineOptions options)
: base(partManager, GetAccessor(options), GetFileProviderAccessor(), NullLoggerFactory.Instance)
{
}
public IList<MetadataReference> GetCompilationReferencesPublic() => GetCompilationReferences();
}
} }
} }