Watch _ViewImports outside the pages root for changes

Fixes #6428
This commit is contained in:
Pranav K 2017-06-26 09:21:33 -07:00
parent d278d6eedf
commit 9acfc8d221
3 changed files with 81 additions and 6 deletions

View File

@ -43,6 +43,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
private IChangeToken GetCompositeChangeToken() private IChangeToken GetCompositeChangeToken()
{ {
if (_actionDescriptorChangeProviders.Length == 1)
{
return _actionDescriptorChangeProviders[0].GetChangeToken();
}
var changeTokens = new IChangeToken[_actionDescriptorChangeProviders.Length]; var changeTokens = new IChangeToken[_actionDescriptorChangeProviders.Length];
for (var i = 0; i < _actionDescriptorChangeProviders.Length; i++) for (var i = 0; i < _actionDescriptorChangeProviders.Length; i++)
{ {

View File

@ -2,8 +2,11 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Razor.Internal; using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
@ -14,11 +17,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{ {
private readonly IFileProvider _fileProvider; private readonly IFileProvider _fileProvider;
private readonly string _searchPattern; private readonly string _searchPattern;
private readonly string[] _additionalFilesToTrack;
public PageActionDescriptorChangeProvider( public PageActionDescriptorChangeProvider(
RazorTemplateEngine templateEngine,
IRazorViewEngineFileProviderAccessor fileProviderAccessor, IRazorViewEngineFileProviderAccessor fileProviderAccessor,
IOptions<RazorPagesOptions> razorPagesOptions) IOptions<RazorPagesOptions> razorPagesOptions)
{ {
if (templateEngine == null)
{
throw new ArgumentNullException(nameof(templateEngine));
}
if (fileProviderAccessor == null) if (fileProviderAccessor == null)
{ {
throw new ArgumentNullException(nameof(fileProviderAccessor)); throw new ArgumentNullException(nameof(fileProviderAccessor));
@ -30,9 +40,35 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
} }
_fileProvider = fileProviderAccessor.FileProvider; _fileProvider = fileProviderAccessor.FileProvider;
_searchPattern = razorPagesOptions.Value.RootDirectory.TrimEnd('/') + "/**/*.cshtml";
var rootDirectory = razorPagesOptions.Value.RootDirectory;
Debug.Assert(!string.IsNullOrEmpty(rootDirectory));
rootDirectory = rootDirectory.TrimEnd('/');
var importFileAtPagesRoot = rootDirectory + "/" + templateEngine.Options.ImportsFileName;
_additionalFilesToTrack = templateEngine.GetImportItems(importFileAtPagesRoot)
.Select(item => item.Path)
.ToArray();
_searchPattern = rootDirectory + "/**/*.cshtml";
} }
public IChangeToken GetChangeToken() => _fileProvider.Watch(_searchPattern); public IChangeToken GetChangeToken()
{
var wildcardChangeToken = _fileProvider.Watch(_searchPattern);
if (_additionalFilesToTrack.Length == 0)
{
return wildcardChangeToken;
}
var changeTokens = new IChangeToken[_additionalFilesToTrack.Length + 1];
for (var i = 0; i < _additionalFilesToTrack.Length; i++)
{
changeTokens[i] = _fileProvider.Watch(_additionalFilesToTrack[i]);
}
changeTokens[changeTokens.Length - 1] = wildcardChangeToken;
return new CompositeChangeToken(changeTokens);
}
} }
} }

View File

@ -1,8 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Razor.Internal; using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;
using Moq; using Moq;
using Xunit; using Xunit;
@ -14,13 +17,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void GetChangeToken_WatchesAllCshtmlFilesUnderFileSystemRoot() public void GetChangeToken_WatchesAllCshtmlFilesUnderFileSystemRoot()
{ {
// Arrange // Arrange
var options = new TestOptionsManager<RazorPagesOptions>();
var fileProvider = new Mock<IFileProvider>(); var fileProvider = new Mock<IFileProvider>();
var templateEngine = new RazorTemplateEngine(
RazorEngine.Create(),
new FileProviderRazorProject(fileProvider.Object));
var options = new TestOptionsManager<RazorPagesOptions>();
var fileProviderAccessor = new Mock<IRazorViewEngineFileProviderAccessor>(); var fileProviderAccessor = new Mock<IRazorViewEngineFileProviderAccessor>();
fileProviderAccessor fileProviderAccessor
.Setup(f => f.FileProvider) .Setup(f => f.FileProvider)
.Returns(fileProvider.Object); .Returns(fileProvider.Object);
var changeProvider = new PageActionDescriptorChangeProvider(fileProviderAccessor.Object, options); var changeProvider = new PageActionDescriptorChangeProvider(templateEngine, fileProviderAccessor.Object, options);
// Act // Act
var changeToken = changeProvider.GetChangeToken(); var changeToken = changeProvider.GetChangeToken();
@ -35,14 +41,17 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
public void GetChangeToken_WatchesAllCshtmlFilesUnderSpecifiedRootDirectory(string rootDirectory) public void GetChangeToken_WatchesAllCshtmlFilesUnderSpecifiedRootDirectory(string rootDirectory)
{ {
// Arrange // Arrange
var fileProvider = new Mock<IFileProvider>();
var templateEngine = new RazorTemplateEngine(
RazorEngine.Create(),
new FileProviderRazorProject(fileProvider.Object));
var options = new TestOptionsManager<RazorPagesOptions>(); var options = new TestOptionsManager<RazorPagesOptions>();
options.Value.RootDirectory = rootDirectory; options.Value.RootDirectory = rootDirectory;
var fileProvider = new Mock<IFileProvider>();
var fileProviderAccessor = new Mock<IRazorViewEngineFileProviderAccessor>(); var fileProviderAccessor = new Mock<IRazorViewEngineFileProviderAccessor>();
fileProviderAccessor fileProviderAccessor
.Setup(f => f.FileProvider) .Setup(f => f.FileProvider)
.Returns(fileProvider.Object); .Returns(fileProvider.Object);
var changeProvider = new PageActionDescriptorChangeProvider(fileProviderAccessor.Object, options); var changeProvider = new PageActionDescriptorChangeProvider(templateEngine, fileProviderAccessor.Object, options);
// Act // Act
var changeToken = changeProvider.GetChangeToken(); var changeToken = changeProvider.GetChangeToken();
@ -50,5 +59,30 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
// Assert // Assert
fileProvider.Verify(f => f.Watch("/pages-base-dir/**/*.cshtml")); fileProvider.Verify(f => f.Watch("/pages-base-dir/**/*.cshtml"));
} }
[Fact]
public void GetChangeToken_WatchesViewImportsOutsidePagesRoot()
{
// Arrange
var fileProvider = new TestFileProvider();
var templateEngine = new RazorTemplateEngine(
RazorEngine.Create(),
new FileProviderRazorProject(fileProvider));
templateEngine.Options.ImportsFileName = "_ViewImports.cshtml";
var options = new TestOptionsManager<RazorPagesOptions>();
options.Value.RootDirectory = "/dir1/dir2";
var fileProviderAccessor = new Mock<IRazorViewEngineFileProviderAccessor>();
fileProviderAccessor
.Setup(f => f.FileProvider)
.Returns(fileProvider);
var changeProvider = new PageActionDescriptorChangeProvider(templateEngine, fileProviderAccessor.Object, options);
// Act & Assert
var compositeChangeToken = Assert.IsType<CompositeChangeToken>(changeProvider.GetChangeToken());
Assert.Collection(compositeChangeToken.ChangeTokens,
changeToken => Assert.Same(fileProvider.GetChangeToken("/dir1/_ViewImports.cshtml"), changeToken),
changeToken => Assert.Same(fileProvider.GetChangeToken("/_ViewImports.cshtml"), changeToken),
changeToken => Assert.Same(fileProvider.GetChangeToken("/dir1/dir2/**/*.cshtml"), changeToken));
}
} }
} }