diff --git a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs index aaf87e13ff..11a628d597 100644 --- a/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs +++ b/src/Microsoft.AspNet.Mvc.Razor.Host/MvcRazorHost.cs @@ -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 - /// - /// Initializes a new instance of with the specified - /// . - /// - /// Contains information about the executing application. - public MvcRazorHost(IApplicationEnvironment appEnvironment) - : this(new PhysicalFileSystem(appEnvironment.ApplicationBasePath)) - { - } -#elif NET45 +#if NET45 /// /// Initializes a new instance of with the specified /// . @@ -62,12 +48,11 @@ namespace Microsoft.AspNet.Mvc.Razor { } #endif - /// /// Initializes a new instance of using the specified . /// /// A rooted at the application base path. - protected internal MvcRazorHost([NotNull] IFileSystem fileSystem) + public MvcRazorHost(IFileSystem fileSystem) : base(new CSharpRazorCodeLanguage()) { _fileSystem = fileSystem; diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/ExpiringFileInfoCache.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/ExpiringFileInfoCache.cs index 0c37c64e9b..68af815d4d 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Compilation/ExpiringFileInfoCache.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/ExpiringFileInfoCache.cs @@ -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 _fileInfoCache = new ConcurrentDictionary(StringComparer.Ordinal); - private readonly PhysicalFileSystem _fileSystem; + private readonly IFileSystem _fileSystem; private readonly TimeSpan _offset; - protected virtual IFileSystem FileSystem + public ExpiringFileInfoCache(IOptionsAccessor 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 optionsAccessor) - { - // TODO: Inject the IFileSystem but only when we get it from the host - _fileSystem = new PhysicalFileSystem(env.ApplicationBasePath); - _offset = optionsAccessor.Options.ExpirationBeforeCheckingFilesOnDisk; - } - /// 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() { diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/IExpiringFileInfoCache.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/IFileInfoCache.cs similarity index 100% rename from src/Microsoft.AspNet.Mvc.Razor/Compilation/IExpiringFileInfoCache.cs rename to src/Microsoft.AspNet.Mvc.Razor/Compilation/IFileInfoCache.cs diff --git a/src/Microsoft.AspNet.Mvc.Razor/OptionDescriptors/RazorViewEngineOptions.cs b/src/Microsoft.AspNet.Mvc.Razor/OptionDescriptors/RazorViewEngineOptions.cs index 53d534ecd7..3585041721 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/OptionDescriptors/RazorViewEngineOptions.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/OptionDescriptors/RazorViewEngineOptions.cs @@ -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; /// /// Controls the caching behavior. @@ -47,5 +49,28 @@ namespace Microsoft.AspNet.Mvc.Razor /// public IList ViewLocationExpanders { get; } = new List(); + + /// + /// Gets or sets the used by to locate Razor files on + /// disk. + /// + /// + /// At startup, this is initialized to an instance of that is rooted at the + /// application root. + /// + public IFileSystem FileSystem + { + get { return _fileSystem; } + + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + _fileSystem = value; + } + } } } diff --git a/src/Microsoft.AspNet.Mvc.Razor/Razor/PreCompileViews/RazorPreCompiler.cs b/src/Microsoft.AspNet.Mvc.Razor/Razor/PreCompileViews/RazorPreCompiler.cs index a30c6ba5a9..82194d760d 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Razor/PreCompileViews/RazorPreCompiler.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Razor/PreCompileViews/RazorPreCompiler.cs @@ -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()) + this(designTimeServiceProvider, + designTimeServiceProvider.GetService(), + designTimeServiceProvider.GetService>()) { } public RazorPreCompiler([NotNull] IServiceProvider designTimeServiceProvider, - [NotNull] IMvcRazorHost host) + [NotNull] IMvcRazorHost host, + [NotNull] IOptionsAccessor optionsAccessor) { _serviceProvider = designTimeServiceProvider; _host = host; var appEnv = _serviceProvider.GetService(); - _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); diff --git a/src/Microsoft.AspNet.Mvc.Razor/ViewStartProvider.cs b/src/Microsoft.AspNet.Mvc.Razor/ViewStartProvider.cs index f99d1a61d7..8116ca486b 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/ViewStartProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/ViewStartProvider.cs @@ -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 optionsAccessor) { - _fileSystem = new PhysicalFileSystem(appEnv.ApplicationBasePath); + _fileSystem = optionsAccessor.Options.FileSystem; _pageFactory = pageFactory; } diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs index d14339ab96..e1c66d5218 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServices.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServices.cs @@ -35,6 +35,8 @@ namespace Microsoft.AspNet.Mvc // Options and core services. // yield return describe.Transient, MvcOptionsSetup>(); + yield return describe.Transient, RazorViewEngineOptionsSetup>(); + yield return describe.Transient(); 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(); // The host is designed to be discarded after consumption and is very inexpensive to initialize. - yield return describe.Transient(); + yield return describe.Transient(serviceProvider => + { + var optionsAccessor = serviceProvider.GetService>(); + return new MvcRazorHost(optionsAccessor.Options.FileSystem); + }); yield return describe.Singleton(); yield return describe.Singleton(); diff --git a/src/Microsoft.AspNet.Mvc/RazorPreCompileModule.cs b/src/Microsoft.AspNet.Mvc/RazorPreCompileModule.cs index 560a157143..0f72b07400 100644 --- a/src/Microsoft.AspNet.Mvc/RazorPreCompileModule.cs +++ b/src/Microsoft.AspNet.Mvc/RazorPreCompileModule.cs @@ -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(); + + var setup = new RazorViewEngineOptionsSetup(appEnv); + var accessor = new OptionsAccessor(new[] { setup }); + sc.AddInstance>(accessor); sc.Add(MvcServices.GetDefaultServices()); var sp = sc.BuildServiceProvider(_appServices); diff --git a/src/Microsoft.AspNet.Mvc/RazorViewEngineOptionsSetup.cs b/src/Microsoft.AspNet.Mvc/RazorViewEngineOptionsSetup.cs new file mode 100644 index 0000000000..d93e10d43c --- /dev/null +++ b/src/Microsoft.AspNet.Mvc/RazorViewEngineOptionsSetup.cs @@ -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 +{ + /// + /// Sets up default options for . + /// + public class RazorViewEngineOptionsSetup : OptionsAction + { + /// + /// Initializes a new instance of . + /// + /// for the application. + 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); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/ExpiringFileInfoCacheTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/ExpiringFileInfoCacheTest.cs index 3efe7db857..7414bf09e7 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/ExpiringFileInfoCacheTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/ExpiringFileInfoCacheTest.cs @@ -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(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 OptionsAccessor { get { - var options = Options; + var options = new RazorViewEngineOptions + { + FileSystem = TestFileSystem + }; var mock = new Mock>(MockBehavior.Strict); mock.Setup(oa => oa.Options).Returns(options); @@ -50,18 +35,18 @@ namespace Microsoft.AspNet.Mvc.Razor public ControllableExpiringFileInfoCache GetCache(IOptionsAccessor 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 optionsAccessor) - : base(env, optionsAccessor) + public ControllableExpiringFileInfoCache(IOptionsAccessor 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 diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewEngineOptionsTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewEngineOptionsTest.cs new file mode 100644 index 0000000000..12ffb3abc4 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/RazorViewEngineOptionsTest.cs @@ -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(() => options.FileSystem = null); + Assert.Equal("value", ex.ParamName); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Test/RazorViewEngineOptionsSetupTest.cs b/test/Microsoft.AspNet.Mvc.Test/RazorViewEngineOptionsSetupTest.cs new file mode 100644 index 0000000000..2226461a36 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.Test/RazorViewEngineOptionsSetupTest.cs @@ -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(); + 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(options.FileSystem); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Test/project.json b/test/Microsoft.AspNet.Mvc.Test/project.json index 6a8a995178..55c791c321 100644 --- a/test/Microsoft.AspNet.Mvc.Test/project.json +++ b/test/Microsoft.AspNet.Mvc.Test/project.json @@ -10,6 +10,10 @@ "test": "Xunit.KRunner" }, "frameworks": { - "aspnet50": { } + "aspnet50": { + "dependencies": { + "Moq": "4.2.1312.1622" + } + } } }