Modify Razor components to use RazorViewEngineOptions.FileSystem

Fixes #1302
This commit is contained in:
Pranav K 2014-10-09 17:02:42 -07:00
parent 4166c8cbc9
commit a633ef4f97
13 changed files with 174 additions and 105 deletions

View File

@ -11,10 +11,6 @@ using Microsoft.AspNet.Razor.Generator.Compiler;
using Microsoft.AspNet.Razor.Parser;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
#if ASPNET50 || ASPNETCORE50
using Microsoft.Framework.Runtime;
#endif
namespace Microsoft.AspNet.Mvc.Razor
{
public class MvcRazorHost : RazorEngineHost, IMvcRazorHost
@ -41,17 +37,7 @@ namespace Microsoft.AspNet.Mvc.Razor
// This field holds the type name without the generic decoration (MyBaseType)
private readonly string _baseType;
#if ASPNET50 || ASPNETCORE50
/// <summary>
/// Initializes a new instance of <see cref="MvcRazorHost"/> with the specified
/// <param name="appEnvironment"/>.
/// </summary>
/// <param name="appEnvironment">Contains information about the executing application.</param>
public MvcRazorHost(IApplicationEnvironment appEnvironment)
: this(new PhysicalFileSystem(appEnvironment.ApplicationBasePath))
{
}
#elif NET45
#if NET45
/// <summary>
/// Initializes a new instance of <see cref="MvcRazorHost"/> with the specified
/// <param name="root"/>.
@ -62,12 +48,11 @@ namespace Microsoft.AspNet.Mvc.Razor
{
}
#endif
/// <summary>
/// Initializes a new instance of <see cref="MvcRazorHost"/> using the specified <paramref name="fileSystem"/>.
/// </summary>
/// <param name="fileSystem">A <see cref="IFileSystem"/> rooted at the application base path.</param>
protected internal MvcRazorHost([NotNull] IFileSystem fileSystem)
public MvcRazorHost(IFileSystem fileSystem)
: base(new CSharpRazorCodeLanguage())
{
_fileSystem = fileSystem;

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Concurrent;
using Microsoft.AspNet.FileSystems;
using Microsoft.Framework.OptionsModel;
using Microsoft.Framework.Runtime;
namespace Microsoft.AspNet.Mvc.Razor
{
@ -17,15 +16,13 @@ namespace Microsoft.AspNet.Mvc.Razor
private readonly ConcurrentDictionary<string, ExpiringFileInfo> _fileInfoCache =
new ConcurrentDictionary<string, ExpiringFileInfo>(StringComparer.Ordinal);
private readonly PhysicalFileSystem _fileSystem;
private readonly IFileSystem _fileSystem;
private readonly TimeSpan _offset;
protected virtual IFileSystem FileSystem
public ExpiringFileInfoCache(IOptionsAccessor<RazorViewEngineOptions> optionsAccessor)
{
get
{
return _fileSystem;
}
_fileSystem = optionsAccessor.Options.FileSystem;
_offset = optionsAccessor.Options.ExpirationBeforeCheckingFilesOnDisk;
}
protected virtual DateTime UtcNow
@ -36,14 +33,6 @@ namespace Microsoft.AspNet.Mvc.Razor
}
}
public ExpiringFileInfoCache(IApplicationEnvironment env,
IOptionsAccessor<RazorViewEngineOptions> optionsAccessor)
{
// TODO: Inject the IFileSystem but only when we get it from the host
_fileSystem = new PhysicalFileSystem(env.ApplicationBasePath);
_offset = optionsAccessor.Options.ExpirationBeforeCheckingFilesOnDisk;
}
/// <inheritdoc />
public IFileInfo GetFileInfo([NotNull] string virtualPath)
{
@ -59,7 +48,7 @@ namespace Microsoft.AspNet.Mvc.Razor
}
else
{
FileSystem.TryGetFileInfo(virtualPath, out fileInfo);
_fileSystem.TryGetFileInfo(virtualPath, out fileInfo);
expiringFileInfo = new ExpiringFileInfo()
{

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNet.FileSystems;
using Microsoft.AspNet.Mvc.Razor.OptionDescriptors;
namespace Microsoft.AspNet.Mvc.Razor
@ -13,6 +14,7 @@ namespace Microsoft.AspNet.Mvc.Razor
public class RazorViewEngineOptions
{
private TimeSpan _expirationBeforeCheckingFilesOnDisk = TimeSpan.FromSeconds(2);
private IFileSystem _fileSystem;
/// <summary>
/// Controls the <see cref="ExpiringFileInfoCache" /> caching behavior.
@ -47,5 +49,28 @@ namespace Microsoft.AspNet.Mvc.Razor
/// </summary>
public IList<ViewLocationExpanderDescriptor> ViewLocationExpanders { get; }
= new List<ViewLocationExpanderDescriptor>();
/// <summary>
/// Gets or sets the <see cref="IFileSystem" /> used by <see cref="RazorViewEngine"/> to locate Razor files on
/// disk.
/// </summary>
/// <remarks>
/// At startup, this is initialized to an instance of <see cref="PhysicalFileSystem"/> that is rooted at the
/// application root.
/// </remarks>
public IFileSystem FileSystem
{
get { return _fileSystem; }
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_fileSystem = value;
}
}
}
}

View File

@ -8,6 +8,7 @@ using Microsoft.AspNet.FileSystems;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.OptionsModel;
using Microsoft.Framework.Runtime;
namespace Microsoft.AspNet.Mvc.Razor
@ -18,29 +19,26 @@ namespace Microsoft.AspNet.Mvc.Razor
private readonly IFileSystem _fileSystem;
private readonly IMvcRazorHost _host;
protected virtual string FileExtension
{
get
{
return ".cshtml";
}
}
public RazorPreCompiler([NotNull] IServiceProvider designTimeServiceProvider) :
this(designTimeServiceProvider, designTimeServiceProvider.GetService<IMvcRazorHost>())
this(designTimeServiceProvider,
designTimeServiceProvider.GetService<IMvcRazorHost>(),
designTimeServiceProvider.GetService<IOptionsAccessor<RazorViewEngineOptions>>())
{
}
public RazorPreCompiler([NotNull] IServiceProvider designTimeServiceProvider,
[NotNull] IMvcRazorHost host)
[NotNull] IMvcRazorHost host,
[NotNull] IOptionsAccessor<RazorViewEngineOptions> optionsAccessor)
{
_serviceProvider = designTimeServiceProvider;
_host = host;
var appEnv = _serviceProvider.GetService<IApplicationEnvironment>();
_fileSystem = new PhysicalFileSystem(appEnv.ApplicationBasePath);
_fileSystem = optionsAccessor.Options.FileSystem;
}
protected virtual string FileExtension { get; } = ".cshtml";
public virtual void CompileViews([NotNull] IBeforeCompileContext context)
{
var descriptors = CreateCompilationDescriptors(context);

View File

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.FileSystems;
using Microsoft.Framework.Runtime;
using Microsoft.Framework.OptionsModel;
namespace Microsoft.AspNet.Mvc.Razor
{
@ -15,10 +15,10 @@ namespace Microsoft.AspNet.Mvc.Razor
private readonly IFileSystem _fileSystem;
private readonly IRazorPageFactory _pageFactory;
public ViewStartProvider(IApplicationEnvironment appEnv,
IRazorPageFactory pageFactory)
public ViewStartProvider(IRazorPageFactory pageFactory,
IOptionsAccessor<RazorViewEngineOptions> optionsAccessor)
{
_fileSystem = new PhysicalFileSystem(appEnv.ApplicationBasePath);
_fileSystem = optionsAccessor.Options.FileSystem;
_pageFactory = pageFactory;
}

View File

@ -35,6 +35,8 @@ namespace Microsoft.AspNet.Mvc
// Options and core services.
//
yield return describe.Transient<IOptionsAction<MvcOptions>, MvcOptionsSetup>();
yield return describe.Transient<IOptionsAction<RazorViewEngineOptions>, RazorViewEngineOptionsSetup>();
yield return describe.Transient<IAssemblyProvider, DefaultAssemblyProvider>();
yield return describe.Transient(typeof(INestedProviderManager<>), typeof(NestedProviderManager<>));
yield return describe.Transient(typeof(INestedProviderManagerAsync<>), typeof(NestedProviderManagerAsync<>));
@ -106,7 +108,11 @@ namespace Microsoft.AspNet.Mvc
yield return describe.Singleton<IFileInfoCache, ExpiringFileInfoCache>();
// The host is designed to be discarded after consumption and is very inexpensive to initialize.
yield return describe.Transient<IMvcRazorHost, MvcRazorHost>();
yield return describe.Transient<IMvcRazorHost>(serviceProvider =>
{
var optionsAccessor = serviceProvider.GetService<IOptionsAccessor<RazorViewEngineOptions>>();
return new MvcRazorHost(optionsAccessor.Options.FileSystem);
});
yield return describe.Singleton<ICompilerCache, CompilerCache>();
yield return describe.Singleton<ICompilationService, RoslynCompilationService>();

View File

@ -6,9 +6,10 @@ using System.Collections.Generic;
using Microsoft.AspNet.Mvc.Razor;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.Framework.Runtime;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Microsoft.Framework.OptionsModel;
using Microsoft.Framework.Runtime;
namespace Microsoft.AspNet.Mvc
{
@ -21,9 +22,16 @@ namespace Microsoft.AspNet.Mvc
_appServices = services;
}
protected virtual string FileExtension { get; } = ".cshtml";
public virtual void BeforeCompile(IBeforeCompileContext context)
{
var sc = new ServiceCollection();
var appEnv = _appServices.GetService<IApplicationEnvironment>();
var setup = new RazorViewEngineOptionsSetup(appEnv);
var accessor = new OptionsAccessor<RazorViewEngineOptions>(new[] { setup });
sc.AddInstance<IOptionsAccessor<RazorViewEngineOptions>>(accessor);
sc.Add(MvcServices.GetDefaultServices());
var sp = sc.BuildServiceProvider(_appServices);

View File

@ -0,0 +1,32 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.FileSystems;
using Microsoft.AspNet.Mvc.Razor;
using Microsoft.Framework.OptionsModel;
using Microsoft.Framework.Runtime;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Sets up default options for <see cref="RazorViewEngineOptions"/>.
/// </summary>
public class RazorViewEngineOptionsSetup : OptionsAction<RazorViewEngineOptions>
{
/// <summary>
/// Initializes a new instance of <see cref="RazorViewEngineOptions"/>.
/// </summary>
/// <param name="applicationEnvironment"><see cref="IApplicationEnvironment"/> for the application.</param>
public RazorViewEngineOptionsSetup(IApplicationEnvironment applicationEnvironment)
: base(options => ConfigureRazor(options, applicationEnvironment))
{
Order = DefaultOrder.DefaultFrameworkSortOrder;
}
private static void ConfigureRazor(RazorViewEngineOptions razorOptions,
IApplicationEnvironment applicationEnvironment)
{
razorOptions.FileSystem = new PhysicalFileSystem(applicationEnvironment.ApplicationBasePath);
}
}
}

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.IO;
using Microsoft.AspNet.FileSystems;
using Microsoft.Framework.OptionsModel;
using Microsoft.Framework.Runtime;
using Moq;
using Xunit;
@ -16,30 +15,16 @@ namespace Microsoft.AspNet.Mvc.Razor
{
private const string FileName = "myView.cshtml";
public IApplicationEnvironment ApplicationEnvironment
{
get
{
var mock = new Mock<IApplicationEnvironment>(MockBehavior.Strict);
mock.Setup(ae => ae.ApplicationBasePath).Returns(Directory.GetCurrentDirectory());
return mock.Object;
}
}
public RazorViewEngineOptions Options
{
get
{
return new RazorViewEngineOptions();
}
}
public DummyFileSystem TestFileSystem { get; } = new DummyFileSystem();
public IOptionsAccessor<RazorViewEngineOptions> OptionsAccessor
{
get
{
var options = Options;
var options = new RazorViewEngineOptions
{
FileSystem = TestFileSystem
};
var mock = new Mock<IOptionsAccessor<RazorViewEngineOptions>>(MockBehavior.Strict);
mock.Setup(oa => oa.Options).Returns(options);
@ -50,18 +35,18 @@ namespace Microsoft.AspNet.Mvc.Razor
public ControllableExpiringFileInfoCache GetCache(IOptionsAccessor<RazorViewEngineOptions> optionsAccessor)
{
return new ControllableExpiringFileInfoCache(ApplicationEnvironment, optionsAccessor);
return new ControllableExpiringFileInfoCache(optionsAccessor);
}
public void CreateFile(string FileName, ControllableExpiringFileInfoCache cache)
public void CreateFile(string fileName)
{
var fileInfo = new DummyFileInfo()
{
Name = FileName,
Name = fileName,
LastModified = DateTime.Now,
};
cache.UnderlyingFileSystem.AddFile(fileInfo);
TestFileSystem.AddFile(fileInfo);
}
public void Sleep(ControllableExpiringFileInfoCache cache, int offsetMilliseconds)
@ -96,7 +81,7 @@ namespace Microsoft.AspNet.Mvc.Razor
// Arrange
var cache = GetCache(OptionsAccessor);
CreateFile(FileName, cache);
CreateFile(FileName);
// Act
var fileInfo1 = cache.GetFileInfo(FileName);
@ -114,12 +99,12 @@ namespace Microsoft.AspNet.Mvc.Razor
// Arrange
var cache = GetCache(OptionsAccessor);
CreateFile(FileName, cache);
CreateFile(FileName);
// Act
var fileInfo1 = cache.GetFileInfo(FileName);
CreateFile(FileName, cache);
CreateFile(FileName);
var fileInfo2 = cache.GetFileInfo(FileName);
@ -139,13 +124,13 @@ namespace Microsoft.AspNet.Mvc.Razor
// Arrange
var cache = GetCache(optionsAccessor);
CreateFile(FileName, cache);
CreateFile(FileName);
// Act
var fileInfo1 = cache.GetFileInfo(FileName);
Sleep(optionsAccessor, cache, 500);
CreateFile(FileName, cache);
CreateFile(FileName);
var fileInfo2 = cache.GetFileInfo(FileName);
@ -164,7 +149,7 @@ namespace Microsoft.AspNet.Mvc.Razor
var cache = GetCache(optionsAccessor);
CreateFile(FileName, cache);
CreateFile(FileName);
// Act
var fileInfo1 = cache.GetFileInfo(FileName);
@ -213,7 +198,7 @@ namespace Microsoft.AspNet.Mvc.Razor
string FileName = "myfile4.cshtml";
var cache = GetCache(optionsAccessor);
CreateFile(FileName, cache);
CreateFile(FileName);
// Act
var fileInfo1 = cache.GetFileInfo(FileName);
@ -254,7 +239,7 @@ namespace Microsoft.AspNet.Mvc.Razor
string FileName = "myfile5.cshtml";
var cache = GetCache(optionsAccessor);
CreateFile(FileName, cache);
CreateFile(FileName);
// Act
var fileInfo1 = cache.GetFileInfo(FileName);
@ -280,7 +265,7 @@ namespace Microsoft.AspNet.Mvc.Razor
string FileName = "myfile6.cshtml";
var cache = GetCache(optionsAccessor);
CreateFile(FileName, cache);
CreateFile(FileName);
// Act
var fileInfo1 = cache.GetFileInfo(FileName);
@ -305,7 +290,7 @@ namespace Microsoft.AspNet.Mvc.Razor
string FileName = "myfile7.cshtml";
var cache = GetCache(optionsAccessor);
CreateFile(FileName, cache);
CreateFile(FileName);
// Act
var fileInfo1 = cache.GetFileInfo(FileName);
@ -322,14 +307,12 @@ namespace Microsoft.AspNet.Mvc.Razor
public class ControllableExpiringFileInfoCache : ExpiringFileInfoCache
{
public ControllableExpiringFileInfoCache(IApplicationEnvironment env,
IOptionsAccessor<RazorViewEngineOptions> optionsAccessor)
: base(env, optionsAccessor)
public ControllableExpiringFileInfoCache(IOptionsAccessor<RazorViewEngineOptions> optionsAccessor)
: base(optionsAccessor)
{
}
private DateTime? _internalUtcNow { get; set; }
private DummyFileSystem _underlyingFileSystem = new DummyFileSystem();
protected override DateTime UtcNow
{
@ -344,14 +327,6 @@ namespace Microsoft.AspNet.Mvc.Razor
}
}
protected override IFileSystem FileSystem
{
get
{
return UnderlyingFileSystem;
}
}
public void Sleep(int milliSeconds)
{
if (milliSeconds <= 0)
@ -361,14 +336,6 @@ namespace Microsoft.AspNet.Mvc.Razor
_internalUtcNow = UtcNow.AddMilliseconds(milliSeconds);
}
public DummyFileSystem UnderlyingFileSystem
{
get
{
return _underlyingFileSystem;
}
}
}
public class DummyFileSystem : IFileSystem

View File

@ -0,0 +1,22 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Xunit;
namespace Microsoft.AspNet.Mvc.Razor
{
public class RazorViewEngineOptionsTest
{
[Fact]
public void FileSystemThrows_IfNullIsAsseigned()
{
// Arrange
var options = new RazorViewEngineOptions();
// Act and Assert
var ex = Assert.Throws<ArgumentNullException>(() => options.FileSystem = null);
Assert.Equal("value", ex.ParamName);
}
}
}

View File

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using Microsoft.AspNet.FileSystems;
using Microsoft.AspNet.Mvc.Razor;
using Microsoft.Framework.Runtime;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc
{
public class RazorViewEngineOptionsSetupTest
{
[Fact]
public void RazorViewEngineOptionsSetup_SetsUpFileSystem()
{
// Arrange
var options = new RazorViewEngineOptions();
var appEnv = new Mock<IApplicationEnvironment>();
appEnv.SetupGet(e => e.ApplicationBasePath)
.Returns(Directory.GetCurrentDirectory());
var optionsSetup = new RazorViewEngineOptionsSetup(appEnv.Object);
// Act
optionsSetup.Invoke(options);
// Assert
Assert.NotNull(options.FileSystem);
Assert.IsType<PhysicalFileSystem>(options.FileSystem);
}
}
}

View File

@ -10,6 +10,10 @@
"test": "Xunit.KRunner"
},
"frameworks": {
"aspnet50": { }
"aspnet50": {
"dependencies": {
"Moq": "4.2.1312.1622"
}
}
}
}