Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Make RazorViewEngineOptions.FileProvider a list instead of a single item
Browse files Browse the repository at this point in the history
Fixes #3806
  • Loading branch information
pranavkm committed Dec 29, 2015
1 parent 1a8d20f commit 4ceeeff
Show file tree
Hide file tree
Showing 13 changed files with 127 additions and 65 deletions.
6 changes: 4 additions & 2 deletions samples/EmbeddedViewSample.Web/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ public void ConfigureServices(IServiceCollection services)

services.Configure<RazorViewEngineOptions>(options =>
{
options.FileProviders.Clear();

// Base namespace matches the resources added to the assembly from the EmbeddedResources folder.
options.FileProvider = new EmbeddedFileProvider(
options.FileProviders.Add(new EmbeddedFileProvider(
GetType().GetTypeInfo().Assembly,
baseNamespace: "EmbeddedViewSample.Web.EmbeddedResources");
baseNamespace: "EmbeddedViewSample.Web.EmbeddedResources"));
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class DefaultCompilerCacheProvider : ICompilerCacheProvider
/// <param name="optionsAccessor">An accessor to the <see cref="RazorViewEngineOptions"/>.</param>
public DefaultCompilerCacheProvider(IOptions<RazorViewEngineOptions> mvcViewOptions)
{
var fileProvider = mvcViewOptions.Value.FileProvider;
var fileProvider = mvcViewOptions.Value.GetCompositeFileProvider();
Cache = new CompilerCache(fileProvider);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public PrecompiledViewsCompilerCacheProvider(
IEnumerable<Assembly> assemblies)
{
_loadContextAccessor = loadContextAccessor;
_fileProvider = mvcViewOptions.Value.FileProvider;
_fileProvider = mvcViewOptions.Value.GetCompositeFileProvider();
_createCache = CreateCache;
_assemblies = assemblies.ToArray();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,15 @@ public class RazorCompilationService : IRazorCompilationService
/// </summary>
/// <param name="compilationService">The <see cref="ICompilationService"/> to compile generated code.</param>
/// <param name="razorHost">The <see cref="IMvcRazorHost"/> to generate code from Razor files.</param>
/// <param name="viewEngineOptions">
/// The <see cref="IFileProvider"/> to read Razor files referenced in error messages.
/// </param>
/// <param name="viewEngineOptions">Accessor to <see cref="RazorViewEngineOptions"/>.</param>
public RazorCompilationService(
ICompilationService compilationService,
IMvcRazorHost razorHost,
IOptions<RazorViewEngineOptions> viewEngineOptions)
{
_compilationService = compilationService;
_razorHost = razorHost;
_fileProvider = viewEngineOptions.Value.FileProvider;
_fileProvider = viewEngineOptions.Value.GetCompositeFileProvider();
}

/// <inheritdoc />
Expand Down Expand Up @@ -105,9 +103,10 @@ internal CompilationResult GetCompilationFailedResult(RelativeFileInfo file, IEn

private DiagnosticMessage CreateDiagnosticMessage(RazorError error, string filePath)
{
var location = error.Location;
return new DiagnosticMessage(
message: error.Message,
formattedMessage: $"{error} ({error.Location.LineIndex},{error.Location.CharacterIndex}) {error.Message}",
formattedMessage: $"{error} ({location.LineIndex},{location.CharacterIndex}) {error.Message}",
filePath: filePath,
startLine: error.Location.LineIndex + 1,
startColumn: error.Location.CharacterIndex,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public RoslynCompilationService(
_environment = environment;
_libraryExporter = libraryExporter;
_applicationReferences = new Lazy<List<MetadataReference>>(GetApplicationReferences);
_fileProvider = optionsAccessor.Value.FileProvider;
_fileProvider = optionsAccessor.Value.GetCompositeFileProvider();
_classPrefix = host.MainClassNamePrefix;
_compilationCallback = optionsAccessor.Value.CompilationCallback;
_parseOptions = optionsAccessor.Value.ParseOptions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.CompilationAbstractions;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.MemoryPool;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.PlatformAbstractions;

Expand Down Expand Up @@ -134,8 +133,8 @@ internal static void AddRazorViewEngineServices(IServiceCollection services)

services.TryAdd(ServiceDescriptor.Singleton<IChunkTreeCache>(serviceProvider =>
{
var cachedFileProvider = serviceProvider.GetRequiredService<IOptions<RazorViewEngineOptions>>();
return new DefaultChunkTreeCache(cachedFileProvider.Value.FileProvider);
var viewEngineOptions = serviceProvider.GetRequiredService<IOptions<RazorViewEngineOptions>>();
return new DefaultChunkTreeCache(viewEngineOptions.Value.GetCompositeFileProvider());
}));

// Caches compilation artifacts across the lifetime of the application.
Expand Down
57 changes: 35 additions & 22 deletions src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ namespace Microsoft.AspNet.Mvc.Razor
/// </summary>
public class RazorViewEngineOptions
{
private IFileProvider _fileProvider;

private CSharpParseOptions _parseOptions = new CSharpParseOptions(LanguageVersion.CSharp6);

private CSharpCompilationOptions _compilationOptions = new CSharpCompilationOptions(CodeAnalysis.OutputKind.DynamicallyLinkedLibrary);
private CSharpCompilationOptions _compilationOptions =
new CSharpCompilationOptions(CodeAnalysis.OutputKind.DynamicallyLinkedLibrary);

private Action<RoslynCompilationContext> _compilationCallback = c => { };
private IFileProvider _fileProvider;

/// <summary>
/// Get a <see cref="IList{IViewLocationExpander}"/> used by the <see cref="RazorViewEngine"/>.
Expand All @@ -29,33 +29,22 @@ public class RazorViewEngineOptions
= new List<IViewLocationExpander>();

/// <summary>
/// Gets or sets the <see cref="IFileProvider" /> used by <see cref="RazorViewEngine"/> to locate Razor files on
/// disk.
/// Gets the sequence of <see cref="IFileProvider" /> instances used by <see cref="RazorViewEngine"/> to
/// locate Razor files.
/// </summary>
/// <remarks>
/// At startup, this is initialized to an instance of <see cref="PhysicalFileProvider"/> that is rooted at the
/// application root.
/// At startup, this is initialized to include an instance of <see cref="PhysicalFileProvider"/> that is
/// rooted at the application root.
/// </remarks>
public IFileProvider FileProvider
{
get { return _fileProvider; }

set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}

_fileProvider = value;
}
}
public IList<IFileProvider> FileProviders { get; } = new List<IFileProvider>();

/// <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.
/// Customizations made here would not reflect in tooling (Intellisense).
/// </summary>
/// <remarks>
/// Customizations made here would not reflect in tooling (Intellisense).
/// </remarks>
public Action<RoslynCompilationContext> CompilationCallback
{
get { return _compilationCallback; }
Expand All @@ -65,6 +54,7 @@ public Action<RoslynCompilationContext> CompilationCallback
{
throw new ArgumentNullException(nameof(value));
}

_compilationCallback = value;
}
}
Expand All @@ -81,6 +71,7 @@ public CSharpParseOptions ParseOptions
{
throw new ArgumentNullException(nameof(value));
}

_parseOptions = value;
}
}
Expand All @@ -97,9 +88,31 @@ public CSharpCompilationOptions CompilationOptions
{
throw new ArgumentNullException(nameof(value));
}

_compilationOptions = value;
}
}

/// <summary>
/// Gets the composite result of <see cref="FileProviders"/>.
/// </summary>
/// <returns>The <see cref="IFileProvider"/> instance if exactly one instance is registered, otherwise
/// a <see cref="CompositeFileProvider"/> that contains all registered instances.</returns>
public IFileProvider GetCompositeFileProvider()
{
if (_fileProvider == null)
{
if (FileProviders.Count == 1)
{
_fileProvider = FileProviders[0];
}
else
{
_fileProvider = new CompositeFileProvider(FileProviders);
}
}

return _fileProvider;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ private static void ConfigureRazor(RazorViewEngineOptions razorOptions,
IApplicationEnvironment applicationEnvironment,
IHostingEnvironment hostingEnvironment)
{
razorOptions.FileProvider = new PhysicalFileProvider(applicationEnvironment.ApplicationBasePath);
razorOptions.FileProviders.Add(new PhysicalFileProvider(applicationEnvironment.ApplicationBasePath));

var parseOptions = new CSharpParseOptions(LanguageVersion.CSharp6);
var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.AspNet.Mvc.Razor/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"keyFile": "../../tools/Key.snk"
},
"dependencies": {
"Microsoft.AspNet.FileProviders.Composite": "1.0.0-*",
"Microsoft.AspNet.Mvc.Razor.Host": "6.0.0-*",
"Microsoft.AspNet.Mvc.ViewFeatures": "6.0.0-*",
"Microsoft.AspNet.Razor.Runtime.Precompilation": "4.0.0-*",
Expand All @@ -26,8 +27,7 @@
"type": "build"
},
"Microsoft.Dnx.Compilation.CSharp.Common": "1.0.0-*",
"Microsoft.Dnx.Compilation.CSharp.Abstractions": "1.0.0-*",
"Microsoft.Extensions.MemoryPool": "1.0.0-*"
"Microsoft.Dnx.Compilation.CSharp.Abstractions": "1.0.0-*"
},
"frameworks": {
"net451": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,8 @@ private static GeneratorResults GetGeneratorResult()

private static IOptions<RazorViewEngineOptions> GetOptions(IFileProvider fileProvider = null)
{
var razorViewEngineOptions = new RazorViewEngineOptions
{
FileProvider = fileProvider ?? new TestFileProvider()
};
var razorViewEngineOptions = new RazorViewEngineOptions();
razorViewEngineOptions.FileProviders.Add(fileProvider ?? new TestFileProvider());
var options = new Mock<IOptions<RazorViewEngineOptions>>();
options.SetupGet(o => o.Value)
.Returns(razorViewEngineOptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,17 +224,16 @@ public void GetCompilationFailedResult_ReturnsCompilationResult_WithGroupedMessa
var generatedCodeFileName = "Generated Code";
var fileProvider = new TestFileProvider();
fileProvider.AddFile(viewPath, "view-content");
var options = new Mock<IOptions<RazorViewEngineOptions>>();
options.SetupGet(o => o.Value)
.Returns(new RazorViewEngineOptions
{
FileProvider = fileProvider
});
var options = new RazorViewEngineOptions();
options.FileProviders.Add(fileProvider);
var optionsAccessor = new Mock<IOptions<RazorViewEngineOptions>>();
optionsAccessor.SetupGet(o => o.Value)
.Returns(options);
var compilationService = new RoslynCompilationService(
PlatformServices.Default.Application,
CompilationServices.Default.LibraryExporter,
Mock.Of<IMvcRazorHost>(),
options.Object);
optionsAccessor.Object);

var assemblyName = "random-assembly-name";

Expand Down Expand Up @@ -355,9 +354,9 @@ private static IOptions<RazorViewEngineOptions> GetOptions(IFileProvider filePro
{
var razorViewEngineOptions = new RazorViewEngineOptions
{
FileProvider = fileProvider ?? new TestFileProvider(),
CompilationCallback = callback ?? (c => { })
CompilationCallback = callback ?? (c => { }),
};
razorViewEngineOptions.FileProviders.Add(fileProvider ?? new TestFileProvider());
var options = new Mock<IOptions<RazorViewEngineOptions>>();
options
.SetupGet(o => o.Value)
Expand Down
68 changes: 59 additions & 9 deletions test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewEngineOptionsTest.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// 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 Microsoft.AspNet.FileProviders;
using Microsoft.AspNet.Mvc.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
Expand All @@ -12,18 +12,27 @@ namespace Microsoft.AspNet.Mvc.Razor
public class RazorViewEngineOptionsTest
{
[Fact]
public void FileProviderThrows_IfNullIsAssigned()
public void AddRazorOptions_ConfiguresOptionsAsExpected()
{
// Arrange
var options = new RazorViewEngineOptions();
var services = new ServiceCollection().AddOptions();
var fileProvider = new TestFileProvider();

// Act and Assert
var ex = Assert.Throws<ArgumentNullException>(() => options.FileProvider = null);
Assert.Equal("value", ex.ParamName);
// Act
var builder = new MvcBuilder(services);
builder.AddRazorOptions(options =>
{
options.FileProviders.Add(fileProvider);
});
var serviceProvider = services.BuildServiceProvider();

// Assert
var accessor = serviceProvider.GetRequiredService<IOptions<RazorViewEngineOptions>>();
Assert.Same(fileProvider, accessor.Value.FileProviders[0]);
}

[Fact]
public void AddRazorOptions_ConfiguresOptionsProperly()
public void GetCompositeFileProvider_ReturnsInstanceIfExactlyOneFileProviderIsSpecified()
{
// Arrange
var services = new ServiceCollection().AddOptions();
Expand All @@ -33,13 +42,54 @@ public void AddRazorOptions_ConfiguresOptionsProperly()
var builder = new MvcBuilder(services);
builder.AddRazorOptions(options =>
{
options.FileProvider = fileProvider;
options.FileProviders.Add(fileProvider);
});
var serviceProvider = services.BuildServiceProvider();

// Assert
var accessor = serviceProvider.GetRequiredService<IOptions<RazorViewEngineOptions>>();
Assert.Same(fileProvider, accessor.Value.GetCompositeFileProvider());
}

[Fact]
public void GetCompositeFileProvider_ReturnsCompositeFileProviderIfNoInstancesAreRegistered()
{
// Arrange
var services = new ServiceCollection().AddOptions();

// Act
var builder = new MvcBuilder(services);
builder.AddRazorOptions(options =>
{
options.FileProviders.Clear();
});
var serviceProvider = services.BuildServiceProvider();

// Assert
var accessor = serviceProvider.GetRequiredService<IOptions<RazorViewEngineOptions>>();
var result = accessor.Value.GetCompositeFileProvider();
Assert.IsType<CompositeFileProvider>(result);
}

[Fact]
public void GetCompositeFileProvider_ReturnsCompositeFileProviderIfMoreThanOneInstanceIsRegistered()
{
// Arrange
var services = new ServiceCollection().AddOptions();

// Act
var builder = new MvcBuilder(services);
builder.AddRazorOptions(options =>
{
options.FileProviders.Add(new TestFileProvider());
options.FileProviders.Add(new TestFileProvider());
});
var serviceProvider = services.BuildServiceProvider();

// Assert
var accessor = serviceProvider.GetRequiredService<IOptions<RazorViewEngineOptions>>();
Assert.Same(fileProvider, accessor.Value.FileProvider);
var result = accessor.Value.GetCompositeFileProvider();
Assert.IsType<CompositeFileProvider>(result);
}
}
}
Loading

0 comments on commit 4ceeeff

Please sign in to comment.