Use dependency context from all application parts when compiling views
Fixes #4498
This commit is contained in:
parent
27565c4e8d
commit
ab76f743f4
|
|
@ -4,13 +4,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="ApplicationPart"/> backed by an <see cref="Assembly"/>.
|
||||
/// </summary>
|
||||
public class AssemblyPart : ApplicationPart, IApplicationPartTypeProvider
|
||||
public class AssemblyPart : ApplicationPart, IApplicationPartTypeProvider, ICompilationLibrariesProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Initalizes a new <see cref="AssemblyPart"/> instance.
|
||||
|
|
@ -38,5 +39,17 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
|||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<TypeInfo> Types => Assembly.DefinedTypes;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<CompilationLibrary> GetCompilationLibraries()
|
||||
{
|
||||
var dependencyContext = DependencyContext.Load(Assembly);
|
||||
if (dependencyContext != null)
|
||||
{
|
||||
return dependencyContext.CompileLibraries;
|
||||
}
|
||||
|
||||
return new CompilationLibrary[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationParts
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes <see cref="CompilationLibrary"/> instances from an <see cref="ApplicationPart"/>.
|
||||
/// </summary>
|
||||
public interface ICompilationLibrariesProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the sequence of <see cref="CompilationLibrary"/> instances.
|
||||
/// </summary>
|
||||
IReadOnlyList<CompilationLibrary> GetCompilationLibraries();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the list of <see cref="MetadataReference"/> used in Razor compilation.
|
||||
/// </summary>
|
||||
public class MetadataReferenceFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the <see cref="MetadataReference"/> instances.
|
||||
/// </summary>
|
||||
public IList<MetadataReference> MetadataReferences { get; } = new List<MetadataReference>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="IApplicationFeatureProvider{TFeature}"/> for <see cref="MetadataReferenceFeature"/> that
|
||||
/// uses <see cref="DependencyContext"/> for registered <see cref="AssemblyPart"/> instances to create
|
||||
/// <see cref="MetadataReference"/>.
|
||||
/// </summary>
|
||||
public class MetadataReferenceFeatureProvider : IApplicationFeatureProvider<MetadataReferenceFeature>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void PopulateFeature(IEnumerable<ApplicationPart> parts, MetadataReferenceFeature feature)
|
||||
{
|
||||
if (parts == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parts));
|
||||
}
|
||||
|
||||
if (feature == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(feature));
|
||||
}
|
||||
|
||||
var libraryPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var providerPart in parts.OfType<ICompilationLibrariesProvider>())
|
||||
{
|
||||
var compileLibraries = providerPart.GetCompilationLibraries();
|
||||
|
||||
for (var i = 0; i < compileLibraries.Count; i++)
|
||||
{
|
||||
var library = compileLibraries[i];
|
||||
var referencePaths = library.ResolveReferencePaths();
|
||||
foreach (var path in referencePaths)
|
||||
{
|
||||
if (libraryPaths.Add(path))
|
||||
{
|
||||
var metadataReference = CreateMetadataReference(path);
|
||||
feature.MetadataReferences.Add(metadataReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static MetadataReference CreateMetadataReference(string path)
|
||||
{
|
||||
using (var stream = File.OpenRead(path))
|
||||
{
|
||||
var moduleMetadata = ModuleMetadata.CreateFromStream(stream, PEStreamOptions.PrefetchMetadata);
|
||||
var assemblyMetadata = AssemblyMetadata.Create(moduleMetadata);
|
||||
|
||||
return assemblyMetadata.GetReference(filePath: path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -67,6 +67,11 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
{
|
||||
builder.PartManager.FeatureProviders.Add(new TagHelperFeatureProvider());
|
||||
}
|
||||
|
||||
if (!builder.PartManager.FeatureProviders.OfType<MetadataReferenceFeatureProvider>().Any())
|
||||
{
|
||||
builder.PartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -7,17 +7,15 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Emit;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
|
@ -29,7 +27,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
|||
/// </summary>
|
||||
public class DefaultRoslynCompilationService : ICompilationService
|
||||
{
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly ApplicationPartManager _partManager;
|
||||
private readonly IFileProvider _fileProvider;
|
||||
private readonly Action<RoslynCompilationContext> _compilationCallback;
|
||||
private readonly CSharpParseOptions _parseOptions;
|
||||
|
|
@ -37,22 +35,22 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
|||
private readonly ILogger _logger;
|
||||
private object _applicationReferencesLock = new object();
|
||||
private bool _applicationReferencesInitialized;
|
||||
private List<MetadataReference> _applicationReferences;
|
||||
private IList<MetadataReference> _applicationReferences;
|
||||
|
||||
/// <summary>
|
||||
/// Initalizes a new instance of the <see cref="DefaultRoslynCompilationService"/> class.
|
||||
/// </summary>
|
||||
/// <param name="environment">The <see cref="IHostingEnvironment"/>.</param>
|
||||
/// <param name="partManager">The <see cref="ApplicationPartManager"/>.</param>
|
||||
/// <param name="optionsAccessor">Accessor to <see cref="RazorViewEngineOptions"/>.</param>
|
||||
/// <param name="fileProviderAccessor">The <see cref="IRazorViewEngineFileProviderAccessor"/>.</param>
|
||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
|
||||
public DefaultRoslynCompilationService(
|
||||
IHostingEnvironment environment,
|
||||
ApplicationPartManager partManager,
|
||||
IOptions<RazorViewEngineOptions> optionsAccessor,
|
||||
IRazorViewEngineFileProviderAccessor fileProviderAccessor,
|
||||
ILoggerFactory loggerFactory)
|
||||
{
|
||||
_hostingEnvironment = environment;
|
||||
_partManager = partManager;
|
||||
_fileProvider = fileProviderAccessor.FileProvider;
|
||||
_compilationCallback = optionsAccessor.Value.CompilationCallback;
|
||||
_parseOptions = optionsAccessor.Value.ParseOptions;
|
||||
|
|
@ -60,7 +58,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
|||
_logger = loggerFactory.CreateLogger<DefaultRoslynCompilationService>();
|
||||
}
|
||||
|
||||
private List<MetadataReference> ApplicationReferences
|
||||
private IList<MetadataReference> ApplicationReferences
|
||||
{
|
||||
get
|
||||
{
|
||||
|
|
@ -152,19 +150,14 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="DependencyContext"/>.
|
||||
/// Gets the sequence of <see cref="MetadataReference"/> instances used for compilation.
|
||||
/// </summary>
|
||||
/// <param name="hostingEnvironment">The <see cref="IHostingEnvironment"/>.</param>
|
||||
/// <returns>The <see cref="DependencyContext"/>.</returns>
|
||||
protected virtual DependencyContext GetDependencyContext(IHostingEnvironment hostingEnvironment)
|
||||
/// <returns>The <see cref="MetadataReference"/> instances.</returns>
|
||||
protected virtual IList<MetadataReference> GetApplicationReferences()
|
||||
{
|
||||
if (hostingEnvironment.ApplicationName != null)
|
||||
{
|
||||
var applicationAssembly = Assembly.Load(new AssemblyName(hostingEnvironment.ApplicationName));
|
||||
return DependencyContext.Load(applicationAssembly);
|
||||
}
|
||||
|
||||
return null;
|
||||
var feature = new MetadataReferenceFeature();
|
||||
_partManager.PopulateFeature(feature);
|
||||
return feature.MetadataReferences;
|
||||
}
|
||||
|
||||
private Assembly LoadStream(MemoryStream assemblyStream, MemoryStream pdbStream)
|
||||
|
|
@ -240,53 +233,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
|||
return diagnostic.Location.GetMappedLineSpan().Path;
|
||||
}
|
||||
|
||||
private List<MetadataReference> GetApplicationReferences()
|
||||
{
|
||||
var metadataReferences = new List<MetadataReference>();
|
||||
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<string>(StringComparer.OrdinalIgnoreCase);
|
||||
for (var i = 0; i < dependencyContext.CompileLibraries.Count; i++)
|
||||
{
|
||||
var library = dependencyContext.CompileLibraries[i];
|
||||
IEnumerable<string> referencePaths;
|
||||
try
|
||||
{
|
||||
referencePaths = library.ResolveReferencePaths();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var path in referencePaths)
|
||||
{
|
||||
if (libraryPaths.Add(path))
|
||||
{
|
||||
metadataReferences.Add(CreateMetadataFileReference(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return metadataReferences;
|
||||
}
|
||||
|
||||
private MetadataReference CreateMetadataFileReference(string path)
|
||||
{
|
||||
using (var stream = File.OpenRead(path))
|
||||
{
|
||||
var moduleMetadata = ModuleMetadata.CreateFromStream(stream, PEStreamOptions.PrefetchMetadata);
|
||||
var assemblyMetadata = AssemblyMetadata.Create(moduleMetadata);
|
||||
|
||||
return assemblyMetadata.GetReference(filePath: path);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsError(Diagnostic diagnostic)
|
||||
{
|
||||
return diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
|
||||
{
|
||||
public class MetadataReferenceFeatureProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void PopulateFeature_ReturnsEmptyList_IfNoAssemblyPartsAreRegistered()
|
||||
{
|
||||
// Arrange
|
||||
var applicationPartManager = new ApplicationPartManager();
|
||||
applicationPartManager.ApplicationParts.Add(Mock.Of<ApplicationPart>());
|
||||
applicationPartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider());
|
||||
var feature = new MetadataReferenceFeature();
|
||||
|
||||
// Act
|
||||
applicationPartManager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(feature.MetadataReferences);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateFeature_ReturnsEmptySequence_IfAssemblyDoesNotPreserveCompilationContext()
|
||||
{
|
||||
// Arrange
|
||||
var applicationPartManager = new ApplicationPartManager();
|
||||
var assemblyPart = new AssemblyPart(typeof(MetadataReferenceFeatureProvider).GetTypeInfo().Assembly);
|
||||
applicationPartManager.ApplicationParts.Add(assemblyPart);
|
||||
applicationPartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider());
|
||||
var feature = new MetadataReferenceFeature();
|
||||
|
||||
// Act
|
||||
applicationPartManager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(feature.MetadataReferences);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PopulateFeature_AddsMetadataReferenceForAssemblyPartsWithDependencyContext()
|
||||
{
|
||||
// Arrange
|
||||
var applicationPartManager = new ApplicationPartManager();
|
||||
var currentAssembly = GetType().GetTypeInfo().Assembly;
|
||||
var assemblyPart1 = new AssemblyPart(currentAssembly);
|
||||
applicationPartManager.ApplicationParts.Add(assemblyPart1);
|
||||
var assemblyPart2 = new AssemblyPart(typeof(MetadataReferenceFeatureProvider).GetTypeInfo().Assembly);
|
||||
applicationPartManager.ApplicationParts.Add(assemblyPart2);
|
||||
applicationPartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider());
|
||||
var feature = new MetadataReferenceFeature();
|
||||
|
||||
// Act
|
||||
applicationPartManager.PopulateFeature(feature);
|
||||
|
||||
// Assert
|
||||
Assert.Contains(
|
||||
feature.MetadataReferences,
|
||||
reference => reference.Display.Equals(currentAssembly.Location));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ using System.Linq;
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
|
||||
using Microsoft.AspNetCore.Razor.Runtime.TagHelpers;
|
||||
|
|
@ -55,6 +56,55 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test.DependencyInjection
|
|||
Assert.Empty(builder.PartManager.ApplicationParts);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddRazorViewEngine_AddsMetadataReferenceFeatureProvider()
|
||||
{
|
||||
// Arrange
|
||||
var services = new ServiceCollection();
|
||||
var builder = services.AddMvcCore();
|
||||
|
||||
// Act
|
||||
builder.AddRazorViewEngine();
|
||||
|
||||
// Assert
|
||||
Assert.Single(builder.PartManager.FeatureProviders.OfType<MetadataReferenceFeatureProvider>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddRazorViewEngine_DoesNotAddMultipleMetadataReferenceFeatureProvider_OnMultipleInvocations()
|
||||
{
|
||||
// Arrange
|
||||
var services = new ServiceCollection();
|
||||
var builder = services.AddMvcCore();
|
||||
|
||||
// Act - 1
|
||||
builder.AddRazorViewEngine();
|
||||
|
||||
// Act - 2
|
||||
builder.AddRazorViewEngine();
|
||||
|
||||
// Assert
|
||||
Assert.Single(builder.PartManager.FeatureProviders.OfType<MetadataReferenceFeatureProvider>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddRazorViewEngine_DoesNotReplaceExistingMetadataReferenceFeatureProvider()
|
||||
{
|
||||
// Arrange
|
||||
var services = new ServiceCollection();
|
||||
var builder = services.AddMvcCore();
|
||||
var metadataReferenceFeatureProvider = new MetadataReferenceFeatureProvider();
|
||||
builder.PartManager.FeatureProviders.Add(metadataReferenceFeatureProvider);
|
||||
|
||||
// Act
|
||||
builder.AddRazorViewEngine();
|
||||
|
||||
// Assert
|
||||
var actual = Assert.Single(
|
||||
builder.PartManager.FeatureProviders.OfType<MetadataReferenceFeatureProvider>());
|
||||
Assert.Same(metadataReferenceFeatureProvider, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddTagHelpersAsServices_ReplacesTagHelperActivatorAndTagHelperTypeResolver()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,13 +2,11 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
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;
|
||||
|
|
@ -26,10 +24,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
|||
var content = @"
|
||||
public class MyTestType {}";
|
||||
|
||||
var compilationService = new TestableRoslynCompilationService(
|
||||
GetDependencyContext(),
|
||||
GetOptions(),
|
||||
GetFileProviderAccessor());
|
||||
var compilationService = GetRoslynCompilationService();
|
||||
var relativeFileInfo = new RelativeFileInfo(
|
||||
new TestFileInfo { PhysicalPath = "SomePath" },
|
||||
"some-relative-path");
|
||||
|
|
@ -53,10 +48,7 @@ this should fail";
|
|||
var fileProvider = new TestFileProvider();
|
||||
var fileInfo = fileProvider.AddFile(viewPath, fileContent);
|
||||
|
||||
var compilationService = new TestableRoslynCompilationService(
|
||||
GetDependencyContext(),
|
||||
GetOptions(),
|
||||
GetFileProviderAccessor(fileProvider));
|
||||
var compilationService = GetRoslynCompilationService(fileProvider: fileProvider);
|
||||
var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path");
|
||||
|
||||
// Act
|
||||
|
|
@ -77,10 +69,7 @@ this should fail";
|
|||
var fileContent = "file content";
|
||||
var content = @"this should fail";
|
||||
|
||||
var compilationService = new TestableRoslynCompilationService(
|
||||
GetDependencyContext(),
|
||||
GetOptions(),
|
||||
GetFileProviderAccessor());
|
||||
var compilationService = GetRoslynCompilationService();
|
||||
var relativeFileInfo = new RelativeFileInfo(
|
||||
new TestFileInfo { Content = fileContent },
|
||||
"some-relative-path");
|
||||
|
|
@ -112,10 +101,7 @@ this should fail";
|
|||
var fileProvider = new TestFileProvider();
|
||||
fileProvider.AddFile(path, mockFileInfo.Object);
|
||||
|
||||
var compilationService = new TestableRoslynCompilationService(
|
||||
GetDependencyContext(),
|
||||
GetOptions(),
|
||||
GetFileProviderAccessor());
|
||||
var compilationService = GetRoslynCompilationService(fileProvider: fileProvider);
|
||||
var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, path);
|
||||
|
||||
// Act
|
||||
|
|
@ -140,14 +126,9 @@ public class MyCustomDefinedClass {}
|
|||
public class MyNonCustomDefinedClass {}
|
||||
#endif
|
||||
";
|
||||
|
||||
var options = GetOptions();
|
||||
options.ParseOptions = options.ParseOptions.WithPreprocessorSymbols("MY_CUSTOM_DEFINE");
|
||||
|
||||
var compilationService = new TestableRoslynCompilationService(
|
||||
GetDependencyContext(),
|
||||
options,
|
||||
GetFileProviderAccessor());
|
||||
var compilationService = GetRoslynCompilationService(options: options);
|
||||
var relativeFileInfo = new RelativeFileInfo(
|
||||
new TestFileInfo { PhysicalPath = "SomePath" },
|
||||
"some-relative-path");
|
||||
|
|
@ -170,12 +151,7 @@ public class MyNonCustomDefinedClass {}
|
|||
fileProvider.AddFile(viewPath, "view-content");
|
||||
var options = new RazorViewEngineOptions();
|
||||
options.FileProviders.Add(fileProvider);
|
||||
|
||||
var compilationService = new TestableRoslynCompilationService(
|
||||
GetDependencyContext(),
|
||||
options,
|
||||
GetFileProviderAccessor(fileProvider));
|
||||
|
||||
var compilationService = GetRoslynCompilationService(options: options, fileProvider: fileProvider);
|
||||
var assemblyName = "random-assembly-name";
|
||||
|
||||
var diagnostics = new[]
|
||||
|
|
@ -256,11 +232,8 @@ public class MyNonCustomDefinedClass {}
|
|||
// Arrange
|
||||
var content = "public class MyTestType {}";
|
||||
RoslynCompilationContext usedCompilation = null;
|
||||
|
||||
var compilationService = new TestableRoslynCompilationService(
|
||||
GetDependencyContext(),
|
||||
GetOptions(callback: c => usedCompilation = c),
|
||||
GetFileProviderAccessor());
|
||||
var options = GetOptions(c => usedCompilation = c);
|
||||
var compilationService = GetRoslynCompilationService(options: options);
|
||||
|
||||
var relativeFileInfo = new RelativeFileInfo(
|
||||
new TestFileInfo { PhysicalPath = "SomePath" },
|
||||
|
|
@ -274,43 +247,12 @@ public class MyNonCustomDefinedClass {}
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void Compile_ThrowsIfDependencyContextIsNullAndTheApplicationFailsToCompileWithNoReferences()
|
||||
public void Compile_ThrowsIfNoMetadataReferencesAreDiscoveredAndApplicationFailsToCompile()
|
||||
{
|
||||
// Arrange
|
||||
var content = "public class MyTestType {}";
|
||||
var compilationService = new TestableRoslynCompilationService(
|
||||
dependencyContext: null,
|
||||
viewEngineOptions: GetOptions(),
|
||||
fileProviderAccessor: GetFileProviderAccessor());
|
||||
|
||||
var relativeFileInfo = new RelativeFileInfo(
|
||||
new TestFileInfo { PhysicalPath = "SomePath" },
|
||||
"some-relative-path.cshtml");
|
||||
|
||||
var expected = "The Razor page 'some-relative-path.cshtml' failed to compile. Ensure that your "
|
||||
+ "application's project.json sets the 'preserveCompilationContext' compilation property.";
|
||||
|
||||
// Act and Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() =>
|
||||
compilationService.Compile(relativeFileInfo, content));
|
||||
Assert.Equal(expected, ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Compile_ThrowsIfDependencyContextReturnsNoReferencesAndTheApplicationFailsToCompile()
|
||||
{
|
||||
// Arrange
|
||||
var content = "public class MyTestType {}";
|
||||
var dependencyContext = new DependencyContext(
|
||||
new TargetInfo("framework", "runtime", "signature", isPortable: true),
|
||||
Extensions.DependencyModel.CompilationOptions.Default,
|
||||
new CompilationLibrary[0],
|
||||
new RuntimeLibrary[0],
|
||||
Enumerable.Empty<RuntimeFallbacks>());
|
||||
var compilationService = new TestableRoslynCompilationService(
|
||||
dependencyContext: dependencyContext,
|
||||
viewEngineOptions: GetOptions(),
|
||||
fileProviderAccessor: GetFileProviderAccessor());
|
||||
var applicationPartManager = new ApplicationPartManager();
|
||||
var compilationService = GetRoslynCompilationService(applicationPartManager);
|
||||
|
||||
var relativeFileInfo = new RelativeFileInfo(
|
||||
new TestFileInfo { PhysicalPath = "SomePath" },
|
||||
|
|
@ -334,11 +276,7 @@ public class MyNonCustomDefinedClass {}
|
|||
context.Compilation = context.Compilation.RemoveAllReferences();
|
||||
});
|
||||
var content = "public class MyTestType {}";
|
||||
var compilationService = new TestableRoslynCompilationService(
|
||||
dependencyContext: GetDependencyContext(),
|
||||
viewEngineOptions: options,
|
||||
fileProviderAccessor: GetFileProviderAccessor());
|
||||
|
||||
var compilationService = GetRoslynCompilationService(options: options);
|
||||
var relativeFileInfo = new RelativeFileInfo(
|
||||
new TestFileInfo { PhysicalPath = "SomePath" },
|
||||
"some-relative-path.cshtml");
|
||||
|
|
@ -363,10 +301,8 @@ public class MyNonCustomDefinedClass {}
|
|||
.AddReferences(MetadataReference.CreateFromFile(assemblyLocation));
|
||||
});
|
||||
var content = "public class MyTestType {}";
|
||||
var compilationService = new TestableRoslynCompilationService(
|
||||
dependencyContext: null,
|
||||
viewEngineOptions: options,
|
||||
fileProviderAccessor: GetFileProviderAccessor());
|
||||
var applicationPartManager = new ApplicationPartManager();
|
||||
var compilationService = GetRoslynCompilationService(applicationPartManager, options);
|
||||
|
||||
var relativeFileInfo = new RelativeFileInfo(
|
||||
new TestFileInfo { PhysicalPath = "SomePath" },
|
||||
|
|
@ -399,7 +335,7 @@ public class MyNonCustomDefinedClass {}
|
|||
};
|
||||
}
|
||||
|
||||
private IRazorViewEngineFileProviderAccessor GetFileProviderAccessor(IFileProvider fileProvider = null)
|
||||
private static IRazorViewEngineFileProviderAccessor GetFileProviderAccessor(IFileProvider fileProvider = null)
|
||||
{
|
||||
var options = new Mock<IRazorViewEngineFileProviderAccessor>();
|
||||
options.SetupGet(o => o.FileProvider)
|
||||
|
|
@ -408,38 +344,36 @@ public class MyNonCustomDefinedClass {}
|
|||
return options.Object;
|
||||
}
|
||||
|
||||
private DependencyContext GetDependencyContext()
|
||||
private static IOptions<RazorViewEngineOptions> GetAccessor(RazorViewEngineOptions options)
|
||||
{
|
||||
var assembly = typeof(DefaultRoslynCompilationServiceTest).GetTypeInfo().Assembly;
|
||||
return DependencyContext.Load(assembly);
|
||||
var optionsAccessor = new Mock<IOptions<RazorViewEngineOptions>>();
|
||||
optionsAccessor.SetupGet(a => a.Value).Returns(options);
|
||||
return optionsAccessor.Object;
|
||||
}
|
||||
|
||||
private class TestableRoslynCompilationService : DefaultRoslynCompilationService
|
||||
private static ApplicationPartManager GetApplicationPartManager()
|
||||
{
|
||||
private readonly DependencyContext _dependencyContext;
|
||||
var applicationPartManager = new ApplicationPartManager();
|
||||
var assembly = typeof(DefaultRoslynCompilationServiceTest).GetTypeInfo().Assembly;
|
||||
applicationPartManager.ApplicationParts.Add(new AssemblyPart(assembly));
|
||||
applicationPartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider());
|
||||
|
||||
public TestableRoslynCompilationService(
|
||||
DependencyContext dependencyContext,
|
||||
RazorViewEngineOptions viewEngineOptions,
|
||||
IRazorViewEngineFileProviderAccessor fileProviderAccessor)
|
||||
: base(
|
||||
Mock.Of<IHostingEnvironment>(),
|
||||
GetAccessor(viewEngineOptions),
|
||||
fileProviderAccessor,
|
||||
NullLoggerFactory.Instance)
|
||||
{
|
||||
_dependencyContext = dependencyContext;
|
||||
}
|
||||
return applicationPartManager;
|
||||
}
|
||||
|
||||
private static IOptions<RazorViewEngineOptions> GetAccessor(RazorViewEngineOptions options)
|
||||
{
|
||||
var optionsAccessor = new Mock<IOptions<RazorViewEngineOptions>>();
|
||||
optionsAccessor.SetupGet(a => a.Value).Returns(options);
|
||||
return optionsAccessor.Object;
|
||||
}
|
||||
private static DefaultRoslynCompilationService GetRoslynCompilationService(
|
||||
ApplicationPartManager partManager = null,
|
||||
RazorViewEngineOptions options = null,
|
||||
IFileProvider fileProvider = null)
|
||||
{
|
||||
partManager = partManager ?? GetApplicationPartManager();
|
||||
options = options ?? GetOptions();
|
||||
|
||||
protected override DependencyContext GetDependencyContext(IHostingEnvironment hostingEnvironment)
|
||||
=> _dependencyContext;
|
||||
return new DefaultRoslynCompilationService(
|
||||
partManager,
|
||||
GetAccessor(options),
|
||||
GetFileProviderAccessor(fileProvider),
|
||||
NullLoggerFactory.Instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ using Microsoft.AspNetCore.Mvc.Filters;
|
|||
using Microsoft.AspNetCore.Mvc.Formatters.Json.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
|
||||
using Microsoft.AspNetCore.Mvc.TagHelpers;
|
||||
|
|
@ -202,10 +203,11 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
Assert.NotNull(descriptor.ImplementationInstance);
|
||||
var manager = Assert.IsType<ApplicationPartManager>(descriptor.ImplementationInstance);
|
||||
|
||||
Assert.Equal(3, manager.FeatureProviders.Count);
|
||||
Assert.IsType<ControllerFeatureProvider>(manager.FeatureProviders[0]);
|
||||
Assert.IsType<ViewComponentFeatureProvider>(manager.FeatureProviders[1]);
|
||||
Assert.IsType<TagHelperFeatureProvider>(manager.FeatureProviders[2]);
|
||||
Assert.Collection(manager.FeatureProviders,
|
||||
feature => Assert.IsType<ControllerFeatureProvider>(feature),
|
||||
feature => Assert.IsType<ViewComponentFeatureProvider>(feature),
|
||||
feature => Assert.IsType<TagHelperFeatureProvider>(feature),
|
||||
feature => Assert.IsType<MetadataReferenceFeatureProvider>(feature));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
{
|
||||
"buildOptions": {
|
||||
"preserveCompilationContext": true
|
||||
},
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.Mvc": "1.0.0-*"
|
||||
},
|
||||
"frameworks": {
|
||||
"net451": {},
|
||||
"net451": { },
|
||||
"netcoreapp1.0": {
|
||||
"imports": [
|
||||
"dnxcore50",
|
||||
"portable-net451+win8"
|
||||
],
|
||||
"dependencies": {}
|
||||
"dependencies": { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace ControllersFromServicesWebSite
|
||||
{
|
||||
public class AssemblyMetadataReferenceFeatureProvider : IApplicationFeatureProvider<MetadataReferenceFeature>
|
||||
{
|
||||
public void PopulateFeature(IEnumerable<ApplicationPart> parts, MetadataReferenceFeature feature)
|
||||
{
|
||||
var currentAssembly = GetType().GetTypeInfo().Assembly;
|
||||
feature.MetadataReferences.Add(MetadataReference.CreateFromFile(currentAssembly.Location));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,10 +25,15 @@ namespace ControllersFromServicesWebSite
|
|||
.AddMvc()
|
||||
.ConfigureApplicationPartManager(manager => manager.ApplicationParts.Clear())
|
||||
.AddApplicationPart(typeof(TimeScheduleController).GetTypeInfo().Assembly)
|
||||
.ConfigureApplicationPartManager(manager => manager.ApplicationParts.Add(new TypesPart(
|
||||
typeof(AnotherController),
|
||||
typeof(ComponentFromServicesViewComponent),
|
||||
typeof(InServicesTagHelper))))
|
||||
.ConfigureApplicationPartManager(manager =>
|
||||
{
|
||||
manager.ApplicationParts.Add(new TypesPart(
|
||||
typeof(AnotherController),
|
||||
typeof(ComponentFromServicesViewComponent),
|
||||
typeof(InServicesTagHelper)));
|
||||
|
||||
manager.FeatureProviders.Add(new AssemblyMetadataReferenceFeatureProvider());
|
||||
})
|
||||
.AddControllersAsServices()
|
||||
.AddViewComponentsAsServices()
|
||||
.AddTagHelpersAsServices();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"buildOptions": {
|
||||
"emitEntryPoint": true,
|
||||
"preserveCompilationContext": true
|
||||
"emitEntryPoint": true
|
||||
},
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "1.0.1-*",
|
||||
|
|
|
|||
Loading…
Reference in New Issue