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

Commit 9acfc8d

Browse files
committed
Watch _ViewImports outside the pages root for changes
Fixes #6428
1 parent d278d6e commit 9acfc8d

File tree

3 files changed

+81
-6
lines changed

3 files changed

+81
-6
lines changed

src/Microsoft.AspNetCore.Mvc.Core/Internal/ActionDescriptorCollectionProvider.cs

+5
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ public ActionDescriptorCollectionProvider(
4343

4444
private IChangeToken GetCompositeChangeToken()
4545
{
46+
if (_actionDescriptorChangeProviders.Length == 1)
47+
{
48+
return _actionDescriptorChangeProviders[0].GetChangeToken();
49+
}
50+
4651
var changeTokens = new IChangeToken[_actionDescriptorChangeProviders.Length];
4752
for (var i = 0; i < _actionDescriptorChangeProviders.Length; i++)
4853
{

src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionDescriptorChangeProvider.cs

+38-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Diagnostics;
6+
using System.Linq;
57
using Microsoft.AspNetCore.Mvc.Infrastructure;
68
using Microsoft.AspNetCore.Mvc.Razor.Internal;
9+
using Microsoft.AspNetCore.Razor.Language;
710
using Microsoft.Extensions.FileProviders;
811
using Microsoft.Extensions.Options;
912
using Microsoft.Extensions.Primitives;
@@ -14,11 +17,18 @@ public class PageActionDescriptorChangeProvider : IActionDescriptorChangeProvide
1417
{
1518
private readonly IFileProvider _fileProvider;
1619
private readonly string _searchPattern;
20+
private readonly string[] _additionalFilesToTrack;
1721

1822
public PageActionDescriptorChangeProvider(
23+
RazorTemplateEngine templateEngine,
1924
IRazorViewEngineFileProviderAccessor fileProviderAccessor,
2025
IOptions<RazorPagesOptions> razorPagesOptions)
2126
{
27+
if (templateEngine == null)
28+
{
29+
throw new ArgumentNullException(nameof(templateEngine));
30+
}
31+
2232
if (fileProviderAccessor == null)
2333
{
2434
throw new ArgumentNullException(nameof(fileProviderAccessor));
@@ -30,9 +40,35 @@ public PageActionDescriptorChangeProvider(
3040
}
3141

3242
_fileProvider = fileProviderAccessor.FileProvider;
33-
_searchPattern = razorPagesOptions.Value.RootDirectory.TrimEnd('/') + "/**/*.cshtml";
43+
44+
var rootDirectory = razorPagesOptions.Value.RootDirectory;
45+
Debug.Assert(!string.IsNullOrEmpty(rootDirectory));
46+
rootDirectory = rootDirectory.TrimEnd('/');
47+
48+
var importFileAtPagesRoot = rootDirectory + "/" + templateEngine.Options.ImportsFileName;
49+
_additionalFilesToTrack = templateEngine.GetImportItems(importFileAtPagesRoot)
50+
.Select(item => item.Path)
51+
.ToArray();
52+
53+
_searchPattern = rootDirectory + "/**/*.cshtml";
3454
}
3555

36-
public IChangeToken GetChangeToken() => _fileProvider.Watch(_searchPattern);
56+
public IChangeToken GetChangeToken()
57+
{
58+
var wildcardChangeToken = _fileProvider.Watch(_searchPattern);
59+
if (_additionalFilesToTrack.Length == 0)
60+
{
61+
return wildcardChangeToken;
62+
}
63+
64+
var changeTokens = new IChangeToken[_additionalFilesToTrack.Length + 1];
65+
for (var i = 0; i < _additionalFilesToTrack.Length; i++)
66+
{
67+
changeTokens[i] = _fileProvider.Watch(_additionalFilesToTrack[i]);
68+
}
69+
70+
changeTokens[changeTokens.Length - 1] = wildcardChangeToken;
71+
return new CompositeChangeToken(changeTokens);
72+
}
3773
}
3874
}

test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionDescriptorChangeProviderTest.cs

+38-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using Microsoft.AspNetCore.Mvc.Razor;
45
using Microsoft.AspNetCore.Mvc.Razor.Internal;
6+
using Microsoft.AspNetCore.Razor.Language;
57
using Microsoft.Extensions.FileProviders;
8+
using Microsoft.Extensions.Primitives;
69
using Moq;
710
using Xunit;
811

@@ -14,13 +17,16 @@ public class PageActionDescriptorChangeProviderTest
1417
public void GetChangeToken_WatchesAllCshtmlFilesUnderFileSystemRoot()
1518
{
1619
// Arrange
17-
var options = new TestOptionsManager<RazorPagesOptions>();
1820
var fileProvider = new Mock<IFileProvider>();
21+
var templateEngine = new RazorTemplateEngine(
22+
RazorEngine.Create(),
23+
new FileProviderRazorProject(fileProvider.Object));
24+
var options = new TestOptionsManager<RazorPagesOptions>();
1925
var fileProviderAccessor = new Mock<IRazorViewEngineFileProviderAccessor>();
2026
fileProviderAccessor
2127
.Setup(f => f.FileProvider)
2228
.Returns(fileProvider.Object);
23-
var changeProvider = new PageActionDescriptorChangeProvider(fileProviderAccessor.Object, options);
29+
var changeProvider = new PageActionDescriptorChangeProvider(templateEngine, fileProviderAccessor.Object, options);
2430

2531
// Act
2632
var changeToken = changeProvider.GetChangeToken();
@@ -35,20 +41,48 @@ public void GetChangeToken_WatchesAllCshtmlFilesUnderFileSystemRoot()
3541
public void GetChangeToken_WatchesAllCshtmlFilesUnderSpecifiedRootDirectory(string rootDirectory)
3642
{
3743
// Arrange
44+
var fileProvider = new Mock<IFileProvider>();
45+
var templateEngine = new RazorTemplateEngine(
46+
RazorEngine.Create(),
47+
new FileProviderRazorProject(fileProvider.Object));
3848
var options = new TestOptionsManager<RazorPagesOptions>();
3949
options.Value.RootDirectory = rootDirectory;
40-
var fileProvider = new Mock<IFileProvider>();
4150
var fileProviderAccessor = new Mock<IRazorViewEngineFileProviderAccessor>();
4251
fileProviderAccessor
4352
.Setup(f => f.FileProvider)
4453
.Returns(fileProvider.Object);
45-
var changeProvider = new PageActionDescriptorChangeProvider(fileProviderAccessor.Object, options);
54+
var changeProvider = new PageActionDescriptorChangeProvider(templateEngine, fileProviderAccessor.Object, options);
4655

4756
// Act
4857
var changeToken = changeProvider.GetChangeToken();
4958

5059
// Assert
5160
fileProvider.Verify(f => f.Watch("/pages-base-dir/**/*.cshtml"));
5261
}
62+
63+
[Fact]
64+
public void GetChangeToken_WatchesViewImportsOutsidePagesRoot()
65+
{
66+
// Arrange
67+
var fileProvider = new TestFileProvider();
68+
var templateEngine = new RazorTemplateEngine(
69+
RazorEngine.Create(),
70+
new FileProviderRazorProject(fileProvider));
71+
templateEngine.Options.ImportsFileName = "_ViewImports.cshtml";
72+
var options = new TestOptionsManager<RazorPagesOptions>();
73+
options.Value.RootDirectory = "/dir1/dir2";
74+
var fileProviderAccessor = new Mock<IRazorViewEngineFileProviderAccessor>();
75+
fileProviderAccessor
76+
.Setup(f => f.FileProvider)
77+
.Returns(fileProvider);
78+
var changeProvider = new PageActionDescriptorChangeProvider(templateEngine, fileProviderAccessor.Object, options);
79+
80+
// Act & Assert
81+
var compositeChangeToken = Assert.IsType<CompositeChangeToken>(changeProvider.GetChangeToken());
82+
Assert.Collection(compositeChangeToken.ChangeTokens,
83+
changeToken => Assert.Same(fileProvider.GetChangeToken("/dir1/_ViewImports.cshtml"), changeToken),
84+
changeToken => Assert.Same(fileProvider.GetChangeToken("/_ViewImports.cshtml"), changeToken),
85+
changeToken => Assert.Same(fileProvider.GetChangeToken("/dir1/dir2/**/*.cshtml"), changeToken));
86+
}
5387
}
5488
}

0 commit comments

Comments
 (0)