Use dependency context from all application parts when compiling views

Fixes #4498
This commit is contained in:
Pranav K 2016-05-02 10:05:11 -07:00
parent 27565c4e8d
commit ab76f743f4
14 changed files with 335 additions and 185 deletions

View File

@ -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];
}
}
}

View File

@ -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();
}
}

View File

@ -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>();
}
}

View File

@ -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);
}
}
}
}

View File

@ -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>

View File

@ -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;

View File

@ -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));
}
}
}

View File

@ -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()
{

View File

@ -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);
}
}
}

View File

@ -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]

View File

@ -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": { }
}
}
}

View File

@ -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));
}
}
}

View File

@ -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();

View File

@ -1,7 +1,6 @@
{
"buildOptions": {
"emitEntryPoint": true,
"preserveCompilationContext": true
"emitEntryPoint": true
},
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1-*",