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 CSharpParseOptions _parseOptions;
private readonly CSharpCompilationOptions _compilationOptions;
private readonly IList<MetadataReference> _additionalMetadataReferences;
private readonly ILogger _logger;
private object _applicationReferencesLock = new object();
private bool _applicationReferencesInitialized;
private IList<MetadataReference> _applicationReferences;
private object _compilationReferencesLock = new object();
private bool _compilationReferencesInitialized;
private IList<MetadataReference> _compilationReferences;
/// <summary>
/// Initalizes a new instance of the <see cref="DefaultRoslynCompilationService"/> 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<DefaultRoslynCompilationService>();
}
private IList<MetadataReference> ApplicationReferences
private IList<MetadataReference> 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 <see cref="MetadataReference"/> instances used for compilation.
/// </summary>
/// <returns>The <see cref="MetadataReference"/> instances.</returns>
protected virtual IList<MetadataReference> GetApplicationReferences()
protected virtual IList<MetadataReference> 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<MetadataReference>(applicationReferences.Count + _additionalMetadataReferences.Count);
compilationReferences.AddRange(applicationReferences);
compilationReferences.AddRange(_additionalMetadataReferences);
return compilationReferences;
}
private Assembly LoadStream(MemoryStream assemblyStream, MemoryStream pdbStream)

View File

@ -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
/// </remarks>
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>
/// Gets or sets the callback that is used to customize Razor compilation
/// 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.
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<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)
{
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<MetadataReference> GetCompilationReferencesPublic() => GetCompilationReferences();
}
}
}