diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Host/MvcRazorTemplateEngine.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Host/MvcRazorTemplateEngine.cs
new file mode 100644
index 0000000000..c20dedef45
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.Razor.Host/MvcRazorTemplateEngine.cs
@@ -0,0 +1,62 @@
+// 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.IO;
+using System.Text;
+using Microsoft.AspNetCore.Mvc.Razor.Internal;
+using Microsoft.AspNetCore.Razor.Evolution;
+
+namespace Microsoft.AspNetCore.Mvc.Razor
+{
+ ///
+ /// A for Mvc Razor views.
+ ///
+ public class MvcRazorTemplateEngine : RazorTemplateEngine
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ /// The .
+ /// The .
+ public MvcRazorTemplateEngine(
+ RazorEngine engine,
+ RazorProject project)
+ : base(engine, project)
+ {
+ Options.DefaultImports = GetDefaultImports();
+ }
+
+ ///
+ public override RazorCodeDocument CreateCodeDocument(RazorProjectItem projectItem)
+ {
+ var codeDocument = base.CreateCodeDocument(projectItem);
+ codeDocument.SetRelativePath(projectItem.Path);
+
+ return codeDocument;
+ }
+
+ private static RazorSourceDocument GetDefaultImports()
+ {
+ using (var stream = new MemoryStream())
+ using (var writer = new StreamWriter(stream, Encoding.UTF8))
+ {
+ writer.WriteLine("@using System");
+ writer.WriteLine("@using System.Linq");
+ writer.WriteLine("@using System.Collections.Generic");
+ writer.WriteLine("@using Microsoft.AspNetCore.Mvc");
+ writer.WriteLine("@using Microsoft.AspNetCore.Mvc.Rendering");
+ writer.WriteLine("@using Microsoft.AspNetCore.Mvc.ViewFeatures");
+ writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper Html");
+ writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json");
+ writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component");
+ writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.IUrlHelper Url");
+ writer.WriteLine("@inject global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider");
+ writer.WriteLine("@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper, Microsoft.AspNetCore.Mvc.Razor");
+ writer.Flush();
+
+ stream.Position = 0;
+ return RazorSourceDocument.ReadFrom(stream, fileName: null, encoding: Encoding.UTF8);
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Host/ViewHierarchyUtility.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Host/ViewHierarchyUtility.cs
deleted file mode 100644
index cebd4b4d02..0000000000
--- a/src/Microsoft.AspNetCore.Mvc.Razor.Host/ViewHierarchyUtility.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-// 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 System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-
-namespace Microsoft.AspNetCore.Mvc.Razor
-{
- ///
- /// Contains methods to locate _ViewStart.cshtml and _ViewImports.cshtml
- ///
- public static class ViewHierarchyUtility
- {
- private const string ViewStartFileName = "_ViewStart.cshtml";
-
- ///
- /// File name of _ViewImports.cshtml file
- ///
- public static readonly string ViewImportsFileName = "_ViewImports.cshtml";
-
- ///
- /// Gets the view start locations that are applicable to the specified path.
- ///
- /// The application relative path of the file to locate
- /// _ViewStarts for.
- /// A sequence of paths that represent potential view start locations.
- ///
- /// This method returns paths starting from the directory of and
- /// moves upwards until it hits the application root.
- /// e.g.
- /// /Views/Home/View.cshtml -> [ /Views/Home/_ViewStart.cshtml, /Views/_ViewStart.cshtml, /_ViewStart.cshtml ]
- ///
- public static IEnumerable GetViewStartLocations(string applicationRelativePath)
- {
- return GetHierarchicalPath(applicationRelativePath, ViewStartFileName);
- }
-
- ///
- /// Gets the locations for _ViewImportss that are applicable to the specified path.
- ///
- /// The application relative path of the file to locate
- /// _ViewImportss for.
- /// A sequence of paths that represent potential _ViewImports locations.
- ///
- /// This method returns paths starting from the directory of and
- /// moves upwards until it hits the application root.
- /// e.g.
- /// /Views/Home/View.cshtml -> [ /Views/Home/_ViewImports.cshtml, /Views/_ViewImports.cshtml,
- /// /_ViewImports.cshtml ]
- ///
- public static IEnumerable GetViewImportsLocations(string applicationRelativePath)
- {
- return GetHierarchicalPath(applicationRelativePath, ViewImportsFileName);
- }
-
- private static IEnumerable GetHierarchicalPath(string relativePath, string fileName)
- {
- if (string.IsNullOrEmpty(relativePath))
- {
- return Enumerable.Empty();
- }
-
- if (relativePath.StartsWith("~/", StringComparison.Ordinal))
- {
- relativePath = relativePath.Substring(2);
- }
-
- if (relativePath.StartsWith("/", StringComparison.Ordinal))
- {
- relativePath = relativePath.Substring(1);
- }
-
- if (string.Equals(Path.GetFileName(relativePath), fileName, StringComparison.OrdinalIgnoreCase))
- {
- // If the specified path is for the file hierarchy being constructed, then the first file that applies
- // to it is in a parent directory.
- relativePath = Path.GetDirectoryName(relativePath);
-
- if (string.IsNullOrEmpty(relativePath))
- {
- return Enumerable.Empty();
- }
- }
-
- var builder = new StringBuilder(relativePath);
- builder.Replace('\\', '/');
-
- if (builder.Length > 0 && builder[0] != '/')
- {
- builder.Insert(0, '/');
- }
-
- var locations = new List();
- for (var index = builder.Length - 1; index >= 0; index--)
- {
- if (builder[index] == '/')
- {
- builder.Length = index + 1;
- builder.Append(fileName);
-
- locations.Add(builder.ToString());
- }
- }
-
- return locations;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/IRazorCompilationService.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/IRazorCompilationService.cs
deleted file mode 100644
index 3826db45c2..0000000000
--- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/IRazorCompilationService.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-// 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.
-
-namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
-{
- ///
- /// Specifies the contracts for a service that compiles Razor files.
- ///
- public interface IRazorCompilationService
- {
- ///
- /// Compiles the razor file located at .
- ///
- /// A instance that represents the file to compile.
- ///
- ///
- /// A that represents the results of parsing and compiling the file.
- ///
- CompilationResult Compile(RelativeFileInfo fileInfo);
- }
-}
diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RelativeFileInfo.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RelativeFileInfo.cs
deleted file mode 100644
index ed3739bf94..0000000000
--- a/src/Microsoft.AspNetCore.Mvc.Razor/Compilation/RelativeFileInfo.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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.Extensions.FileProviders;
-
-namespace Microsoft.AspNetCore.Mvc.Razor.Compilation
-{
- ///
- /// A container type that represents along with the application base relative path
- /// for a file in the file system.
- ///
- public class RelativeFileInfo
- {
- ///
- /// Initializes a new instance of .
- ///
- /// for the file.
- /// Path of the file relative to the application base.
- public RelativeFileInfo(IFileInfo fileInfo, string relativePath)
- {
- if (fileInfo == null)
- {
- throw new ArgumentNullException(nameof(fileInfo));
- }
-
- if (string.IsNullOrEmpty(relativePath))
- {
- throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(relativePath));
- }
-
- FileInfo = fileInfo;
- RelativePath = relativePath;
- }
-
- ///
- /// Gets the associated with this instance of .
- ///
- public IFileInfo FileInfo { get; }
-
- ///
- /// Gets the path of the file relative to the application base.
- ///
- public string RelativePath { get; }
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs
index 19e897f5e9..cabcbbb0c6 100644
--- a/src/Microsoft.AspNetCore.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Razor/DependencyInjection/MvcRazorMvcCoreBuilderExtensions.cs
@@ -170,12 +170,8 @@ namespace Microsoft.Extensions.DependencyInjection
// In the default scenario the following services are singleton by virtue of being initialized as part of
// creating the singleton RazorViewEngine instance.
services.TryAddTransient();
- services.TryAddTransient();
- services.TryAddSingleton(s =>
- {
- return new DefaultRazorProject(s.GetRequiredService().FileProvider);
- });
+ services.TryAddSingleton();
services.TryAddSingleton(s =>
{
diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/CompilerCache.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/CompilerCache.cs
index 5a91909aae..dfb5607e01 100644
--- a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/CompilerCache.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/CompilerCache.cs
@@ -67,16 +67,16 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
///
public CompilerCacheResult GetOrAdd(
string relativePath,
- Func compile)
+ Func cacheContextFactory)
{
if (relativePath == null)
{
throw new ArgumentNullException(nameof(relativePath));
}
- if (compile == null)
+ if (cacheContextFactory == null)
{
- throw new ArgumentNullException(nameof(compile));
+ throw new ArgumentNullException(nameof(cacheContextFactory));
}
Task cacheEntry;
@@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var normalizedPath = GetNormalizedPath(relativePath);
if (!_cache.TryGetValue(normalizedPath, out cacheEntry))
{
- cacheEntry = CreateCacheEntry(relativePath, normalizedPath, compile);
+ cacheEntry = CreateCacheEntry(normalizedPath, cacheContextFactory);
}
}
@@ -97,14 +97,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
}
private Task CreateCacheEntry(
- string relativePath,
string normalizedPath,
- Func compile)
+ Func cacheContextFactory)
{
TaskCompletionSource compilationTaskSource = null;
- MemoryCacheEntryOptions cacheEntryOptions = null;
- IFileInfo fileInfo = null;
+ MemoryCacheEntryOptions cacheEntryOptions;
Task cacheEntry;
+ CompilerCacheContext compilerCacheContext;
// Safe races cannot be allowed when compiling Razor pages. To ensure only one compilation request succeeds
// per file, we'll lock the creation of a cache entry. Creating the cache entry should be very quick. The
@@ -125,40 +124,39 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
throw new InvalidOperationException(message);
}
- fileInfo = _fileProvider.GetFileInfo(normalizedPath);
- if (!fileInfo.Exists)
- {
- var expirationToken = _fileProvider.Watch(normalizedPath);
- cacheEntry = Task.FromResult(new CompilerCacheResult(new[] { expirationToken }));
+ cacheEntryOptions = new MemoryCacheEntryOptions();
- cacheEntryOptions = new MemoryCacheEntryOptions();
- cacheEntryOptions.AddExpirationToken(expirationToken);
+ compilerCacheContext = cacheContextFactory(normalizedPath);
+ cacheEntryOptions.ExpirationTokens.Add(_fileProvider.Watch(compilerCacheContext.ProjectItem.Path));
+ if (!compilerCacheContext.ProjectItem.Exists)
+ {
+ cacheEntry = Task.FromResult(new CompilerCacheResult(normalizedPath, cacheEntryOptions.ExpirationTokens));
}
else
{
- cacheEntryOptions = GetMemoryCacheEntryOptions(normalizedPath);
-
// A file exists and needs to be compiled.
compilationTaskSource = new TaskCompletionSource();
+ foreach (var projectItem in compilerCacheContext.AdditionalCompilationItems)
+ {
+ cacheEntryOptions.ExpirationTokens.Add(_fileProvider.Watch(projectItem.Path));
+ }
cacheEntry = compilationTaskSource.Task;
}
- cacheEntry = _cache.Set>(normalizedPath, cacheEntry, cacheEntryOptions);
+ cacheEntry = _cache.Set(normalizedPath, cacheEntry, cacheEntryOptions);
}
if (compilationTaskSource != null)
{
- // Indicates that the file was found and needs to be compiled.
- Debug.Assert(fileInfo != null && fileInfo.Exists);
+ // Indicates that a file was found and needs to be compiled.
Debug.Assert(cacheEntryOptions != null);
- var relativeFileInfo = new RelativeFileInfo(fileInfo, normalizedPath);
try
{
- var compilationResult = compile(relativeFileInfo);
+ var compilationResult = compilerCacheContext.Compile(compilerCacheContext);
compilationResult.EnsureSuccessful();
compilationTaskSource.SetResult(
- new CompilerCacheResult(relativePath, compilationResult, cacheEntryOptions.ExpirationTokens));
+ new CompilerCacheResult(normalizedPath, compilationResult, cacheEntryOptions.ExpirationTokens));
}
catch (Exception ex)
{
@@ -169,20 +167,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
return cacheEntry;
}
- private MemoryCacheEntryOptions GetMemoryCacheEntryOptions(string relativePath)
- {
- var options = new MemoryCacheEntryOptions();
- options.AddExpirationToken(_fileProvider.Watch(relativePath));
-
- var viewImportsPaths = ViewHierarchyUtility.GetViewImportsLocations(relativePath);
- foreach (var location in viewImportsPaths)
- {
- options.AddExpirationToken(_fileProvider.Watch(location));
- }
-
- return options;
- }
-
private string GetNormalizedPath(string relativePath)
{
Debug.Assert(relativePath != null);
diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/CompilerCacheContext.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/CompilerCacheContext.cs
new file mode 100644
index 0000000000..1dff582dce
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/CompilerCacheContext.cs
@@ -0,0 +1,29 @@
+// 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 System.Collections.Generic;
+using Microsoft.AspNetCore.Mvc.Razor.Compilation;
+using Microsoft.AspNetCore.Razor.Evolution;
+
+namespace Microsoft.AspNetCore.Mvc.Razor.Internal
+{
+ public struct CompilerCacheContext
+ {
+ public CompilerCacheContext(
+ RazorProjectItem projectItem,
+ IEnumerable additionalCompilationItems,
+ Func compile)
+ {
+ ProjectItem = projectItem;
+ AdditionalCompilationItems = additionalCompilationItems;
+ Compile = compile;
+ }
+
+ public RazorProjectItem ProjectItem { get; }
+
+ public IEnumerable AdditionalCompilationItems { get; }
+
+ public Func Compile { get; }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/CompilerCacheResult.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/CompilerCacheResult.cs
index df8a222434..a1f444721c 100644
--- a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/CompilerCacheResult.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/CompilerCacheResult.cs
@@ -3,8 +3,6 @@
using System;
using System.Collections.Generic;
-using System.Linq.Expressions;
-using System.Reflection;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.Extensions.Primitives;
@@ -18,21 +16,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
{
///
/// Initializes a new instance of with the specified
- /// .
+ /// .
///
/// Path of the view file relative to the application base.
- /// The .
- public CompilerCacheResult(string relativePath, CompilationResult compilationResult)
- : this(relativePath, compilationResult, EmptyArray.Instance)
- {
- }
-
- ///
- /// Initializes a new instance of with the specified
- /// .
- ///
- /// Path of the view file relative to the application base.
- /// The .
+ /// The .
/// true if the view is precompiled, false otherwise.
public CompilerCacheResult(string relativePath, CompilationResult compilationResult, bool isPrecompiled)
: this(relativePath, compilationResult, EmptyArray.Instance)
@@ -42,10 +29,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
///
/// Initializes a new instance of with the specified
- /// .
+ /// .
///
/// Path of the view file relative to the application base.
- /// The .
+ /// The .
/// One or more instances that indicate when
/// this result has expired.
public CompilerCacheResult(string relativePath, CompilationResult compilationResult, IList expirationTokens)
@@ -55,18 +42,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
throw new ArgumentNullException(nameof(expirationTokens));
}
+ RelativePath = relativePath;
+ CompiledType = compilationResult.CompiledType;
ExpirationTokens = expirationTokens;
- var compiledType = compilationResult.CompiledType;
-
- var newExpression = Expression.New(compiledType);
-
- var pathProperty = compiledType.GetProperty(nameof(IRazorPage.Path));
-
- var propertyBindExpression = Expression.Bind(pathProperty, Expression.Constant(relativePath));
- var objectInitializeExpression = Expression.MemberInit(newExpression, propertyBindExpression);
- PageFactory = Expression
- .Lambda>(objectInitializeExpression)
- .Compile();
IsPrecompiled = false;
}
@@ -74,9 +52,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
/// Initializes a new instance of for a file that could not be
/// found in the file system.
///
+ /// Path of the view file relative to the application base.
/// One or more instances that indicate when
/// this result has expired.
- public CompilerCacheResult(IList expirationTokens)
+ public CompilerCacheResult(string relativePath, IList expirationTokens)
{
if (expirationTokens == null)
{
@@ -84,7 +63,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
}
ExpirationTokens = expirationTokens;
- PageFactory = null;
+ RelativePath = null;
+ CompiledType = null;
IsPrecompiled = false;
}
@@ -96,12 +76,17 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
///
/// Gets a value that determines if the view was successfully found and compiled.
///
- public bool Success => PageFactory != null;
+ public bool Success => CompiledType != null;
///
- /// Gets a delegate that creates an instance of the .
+ /// Normalized relative path of the file.
///
- public Func PageFactory { get; }
+ public string RelativePath { get; }
+
+ ///
+ /// The compiled .
+ ///
+ public Type CompiledType { get; }
///
/// Gets a value that determines if the view is precompiled.
diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRazorPageFactoryProvider.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRazorPageFactoryProvider.cs
index 4b26111089..8c122feb23 100644
--- a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRazorPageFactoryProvider.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRazorPageFactoryProvider.cs
@@ -2,8 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Linq.Expressions;
+using System.Reflection;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
-using Microsoft.AspNetCore.Mvc.Razor.Internal;
+using Microsoft.AspNetCore.Razor.Evolution;
namespace Microsoft.AspNetCore.Mvc.Razor.Internal
{
@@ -13,37 +15,26 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
///
public class DefaultRazorPageFactoryProvider : IRazorPageFactoryProvider
{
- ///
- /// This delegate holds on to an instance of .
- ///
- private readonly Func _compileDelegate;
- private readonly ICompilerCacheProvider _compilerCacheProvider;
- private ICompilerCache _compilerCache;
+ private const string ViewImportsFileName = "_ViewImports.cshtml";
+ private readonly RazorCompiler _razorCompiler;
///
/// Initializes a new instance of .
///
- /// The .
+ /// The .
+ /// The .
+ /// The .
/// The .
public DefaultRazorPageFactoryProvider(
- IRazorCompilationService razorCompilationService,
+ RazorEngine razorEngine,
+ RazorProject razorProject,
+ ICompilationService compilationService,
ICompilerCacheProvider compilerCacheProvider)
{
- _compileDelegate = razorCompilationService.Compile;
- _compilerCacheProvider = compilerCacheProvider;
- }
+ var templateEngine = new MvcRazorTemplateEngine(razorEngine, razorProject);
+ templateEngine.Options.ImportsFileName = ViewImportsFileName;
- private ICompilerCache CompilerCache
- {
- get
- {
- if (_compilerCache == null)
- {
- _compilerCache = _compilerCacheProvider.Cache;
- }
-
- return _compilerCache;
- }
+ _razorCompiler = new RazorCompiler(compilationService, compilerCacheProvider, templateEngine);
}
///
@@ -59,10 +50,23 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// For tilde slash paths, drop the leading ~ to make it work with the underlying IFileProvider.
relativePath = relativePath.Substring(1);
}
- var result = CompilerCache.GetOrAdd(relativePath, _compileDelegate);
+
+ var result = _razorCompiler.Compile(relativePath);
if (result.Success)
{
- return new RazorPageFactoryResult(result.PageFactory, result.ExpirationTokens, result.IsPrecompiled);
+ var compiledType = result.CompiledType;
+
+ var newExpression = Expression.New(compiledType);
+ var pathProperty = compiledType.GetTypeInfo().GetProperty(nameof(IRazorPage.Path));
+
+ // Generate: page.Path = relativePath;
+ // Use the normalized path specified from the result.
+ var propertyBindExpression = Expression.Bind(pathProperty, Expression.Constant(result.RelativePath));
+ var objectInitializeExpression = Expression.MemberInit(newExpression, propertyBindExpression);
+ var pageFactory = Expression
+ .Lambda>(objectInitializeExpression)
+ .Compile();
+ return new RazorPageFactoryResult(pageFactory, result.ExpirationTokens, result.IsPrecompiled);
}
else
{
diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRazorProject.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRazorProject.cs
index 3508f90bca..c6096be4c4 100644
--- a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRazorProject.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/DefaultRazorProject.cs
@@ -14,7 +14,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
private const string RazorFileExtension = ".cshtml";
private readonly IFileProvider _provider;
- public DefaultRazorProject(IFileProvider provider)
+ public DefaultRazorProject(IRazorViewEngineFileProviderAccessor accessor)
+ : this(accessor.FileProvider)
+ {
+ }
+
+ // Internal for unit testing
+ internal DefaultRazorProject(IFileProvider provider)
{
_provider = provider;
}
diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/ICompilerCache.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/ICompilerCache.cs
index 13cd73239b..a5d42b2916 100644
--- a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/ICompilerCache.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/ICompilerCache.cs
@@ -20,6 +20,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
/// A cached .
CompilerCacheResult GetOrAdd(
string relativePath,
- Func compile);
+ Func compile);
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/RazorCompilationService.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/RazorCompilationService.cs
deleted file mode 100644
index 4fafa26f4a..0000000000
--- a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/RazorCompilationService.cs
+++ /dev/null
@@ -1,203 +0,0 @@
-// 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 System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Text;
-using Microsoft.AspNetCore.Diagnostics;
-using Microsoft.AspNetCore.Mvc.Razor.Compilation;
-using Microsoft.AspNetCore.Razor.Evolution;
-using Microsoft.Extensions.FileProviders;
-using Microsoft.Extensions.Logging;
-
-namespace Microsoft.AspNetCore.Mvc.Razor.Internal
-{
- ///
- /// Default implementation of .
- ///
- public class RazorCompilationService : IRazorCompilationService
- {
- private readonly ICompilationService _compilationService;
- private readonly RazorEngine _engine;
- private readonly RazorProject _project;
- private readonly IFileProvider _fileProvider;
- private readonly ILogger _logger;
-
- ///
- /// Instantiates a new instance of the class.
- ///
- /// The to compile generated code.
- /// The to generate code from Razor files.
- /// The implementation for locating files.
- /// The .
- /// The .
- public RazorCompilationService(
- ICompilationService compilationService,
- RazorEngine engine,
- RazorProject project,
- IRazorViewEngineFileProviderAccessor fileProviderAccessor,
- ILoggerFactory loggerFactory)
- {
- _compilationService = compilationService;
- _engine = engine;
- _fileProvider = fileProviderAccessor.FileProvider;
- _logger = loggerFactory.CreateLogger();
-
- _project = project;
-
- var stream = new MemoryStream();
- var writer = new StreamWriter(stream, Encoding.UTF8);
- writer.WriteLine("@using System");
- writer.WriteLine("@using System.Linq");
- writer.WriteLine("@using System.Collections.Generic");
- writer.WriteLine("@using Microsoft.AspNetCore.Mvc");
- writer.WriteLine("@using Microsoft.AspNetCore.Mvc.Rendering");
- writer.WriteLine("@using Microsoft.AspNetCore.Mvc.ViewFeatures");
- writer.WriteLine("@inject Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper Html");
- writer.WriteLine("@inject Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json");
- writer.WriteLine("@inject Microsoft.AspNetCore.Mvc.IViewComponentHelper Component");
- writer.WriteLine("@inject Microsoft.AspNetCore.Mvc.IUrlHelper Url");
- writer.WriteLine("@inject Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider");
- writer.WriteLine("@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper, Microsoft.AspNetCore.Mvc.Razor");
- writer.Flush();
-
- stream.Seek(0L, SeekOrigin.Begin);
- GlobalImports = RazorSourceDocument.ReadFrom(stream, fileName: null, encoding: Encoding.UTF8);
- }
-
- public RazorSourceDocument GlobalImports { get; }
-
- ///
- public CompilationResult Compile(RelativeFileInfo file)
- {
- if (file == null)
- {
- throw new ArgumentNullException(nameof(file));
- }
-
- RazorCodeDocument codeDocument;
- RazorCSharpDocument cSharpDocument;
- using (var inputStream = file.FileInfo.CreateReadStream())
- {
- _logger.RazorFileToCodeCompilationStart(file.RelativePath);
-
- var startTimestamp = _logger.IsEnabled(LogLevel.Debug) ? Stopwatch.GetTimestamp() : 0;
-
- codeDocument = CreateCodeDocument(file.RelativePath, inputStream);
- cSharpDocument = ProcessCodeDocument(codeDocument);
-
- _logger.RazorFileToCodeCompilationEnd(file.RelativePath, startTimestamp);
- }
-
- if (cSharpDocument.Diagnostics.Count > 0)
- {
- return GetCompilationFailedResult(file.RelativePath, cSharpDocument.Diagnostics);
- }
-
- return _compilationService.Compile(codeDocument, cSharpDocument);
- }
-
- public virtual RazorCodeDocument CreateCodeDocument(string relativePath, Stream inputStream)
- {
- var absolutePath = _fileProvider.GetFileInfo(relativePath)?.PhysicalPath ?? relativePath;
-
- var source = RazorSourceDocument.ReadFrom(inputStream, absolutePath);
-
- var imports = new List()
- {
- GlobalImports,
- };
-
- var paths = ViewHierarchyUtility.GetViewImportsLocations(relativePath);
- foreach (var path in paths.Reverse())
- {
- var file = _fileProvider.GetFileInfo(path);
- if (file.Exists)
- {
- using (var stream = file.CreateReadStream())
- {
- imports.Add(RazorSourceDocument.ReadFrom(stream, file.PhysicalPath ?? path));
- }
- }
- }
-
- var codeDocument = RazorCodeDocument.Create(source, imports);
- codeDocument.SetRelativePath(relativePath);
- return codeDocument;
- }
-
- public virtual RazorCSharpDocument ProcessCodeDocument(RazorCodeDocument codeDocument)
- {
- _engine.Process(codeDocument);
-
- return codeDocument.GetCSharpDocument();
- }
-
- // Internal for unit testing
- public CompilationResult GetCompilationFailedResult(
- string relativePath,
- IEnumerable errors)
- {
- // If a SourceLocation does not specify a file path, assume it is produced
- // from parsing the current file.
- var messageGroups = errors
- .GroupBy(razorError =>
- razorError.Span.FilePath ?? relativePath,
- StringComparer.Ordinal);
-
- var failures = new List();
- foreach (var group in messageGroups)
- {
- var filePath = group.Key;
- var fileContent = ReadFileContentsSafely(filePath);
- var compilationFailure = new CompilationFailure(
- filePath,
- fileContent,
- compiledContent: string.Empty,
- messages: group.Select(parserError => CreateDiagnosticMessage(parserError, filePath)));
- failures.Add(compilationFailure);
- }
-
- return new CompilationResult(failures);
- }
-
- private DiagnosticMessage CreateDiagnosticMessage(
- RazorDiagnostic error,
- string filePath)
- {
- var sourceSpan = error.Span;
- return new DiagnosticMessage(
- message: error.GetMessage(),
- formattedMessage: $"{error} ({sourceSpan.LineIndex},{sourceSpan.CharacterIndex}) {error.GetMessage()}",
- filePath: filePath,
- startLine: sourceSpan.LineIndex + 1,
- startColumn: sourceSpan.CharacterIndex,
- endLine: sourceSpan.LineIndex + 1,
- endColumn: sourceSpan.CharacterIndex + sourceSpan.Length);
- }
-
- private string ReadFileContentsSafely(string relativePath)
- {
- var fileInfo = _fileProvider.GetFileInfo(relativePath);
- if (fileInfo.Exists)
- {
- try
- {
- using (var reader = new StreamReader(fileInfo.CreateReadStream()))
- {
- return reader.ReadToEnd();
- }
- }
- catch
- {
- // Ignore any failures
- }
- }
-
- return null;
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Internal/RazorCompiler.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/RazorCompiler.cs
new file mode 100644
index 0000000000..873901b1f5
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.Razor/Internal/RazorCompiler.cs
@@ -0,0 +1,132 @@
+
+// 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 System.Collections.Generic;
+using System.Linq;
+using Microsoft.AspNetCore.Diagnostics;
+using Microsoft.AspNetCore.Mvc.Razor.Compilation;
+using Microsoft.AspNetCore.Razor.Evolution;
+
+namespace Microsoft.AspNetCore.Mvc.Razor.Internal
+{
+ public class RazorCompiler
+ {
+ private readonly ICompilationService _compilationService;
+ private readonly ICompilerCacheProvider _compilerCacheProvider;
+ private readonly MvcRazorTemplateEngine _templateEngine;
+ private readonly Func _getCacheContext;
+ private readonly Func _getCompilationResultDelegate;
+
+ public RazorCompiler(
+ ICompilationService compilationService,
+ ICompilerCacheProvider compilerCacheProvider,
+ MvcRazorTemplateEngine templateEngine)
+ {
+ _compilationService = compilationService;
+ _compilerCacheProvider = compilerCacheProvider;
+ _templateEngine = templateEngine;
+ _getCacheContext = GetCacheContext;
+ _getCompilationResultDelegate = GetCompilationResult;
+ }
+
+ private ICompilerCache CompilerCache => _compilerCacheProvider.Cache;
+
+ public CompilerCacheResult Compile(string relativePath)
+ {
+ return CompilerCache.GetOrAdd(relativePath, _getCacheContext);
+ }
+
+ private CompilerCacheContext GetCacheContext(string path)
+ {
+ var item = _templateEngine.Project.GetItem(path);
+ var imports = _templateEngine.Project.FindHierarchicalItems(path, _templateEngine.Options.ImportsFileName);
+ return new CompilerCacheContext(item, imports, GetCompilationResult);
+ }
+
+ private CompilationResult GetCompilationResult(CompilerCacheContext cacheContext)
+ {
+ var projectItem = cacheContext.ProjectItem;
+ var codeDocument = _templateEngine.CreateCodeDocument(projectItem.Path);
+ var cSharpDocument = _templateEngine.GenerateCode(codeDocument);
+
+ CompilationResult compilationResult;
+ if (cSharpDocument.Diagnostics.Count > 0)
+ {
+ compilationResult = GetCompilationFailedResult(
+ codeDocument,
+ cSharpDocument.Diagnostics);
+ }
+ else
+ {
+ compilationResult = _compilationService.Compile(codeDocument, cSharpDocument);
+ }
+
+ return compilationResult;
+ }
+
+ internal CompilationResult GetCompilationFailedResult(
+ RazorCodeDocument codeDocument,
+ IEnumerable diagnostics)
+ {
+ // If a SourceLocation does not specify a file path, assume it is produced from parsing the current file.
+ var messageGroups = diagnostics.GroupBy(
+ razorError => razorError.Span.FilePath ?? codeDocument.Source.FileName,
+ StringComparer.Ordinal);
+
+ var failures = new List();
+ foreach (var group in messageGroups)
+ {
+ var filePath = group.Key;
+ var fileContent = ReadContent(codeDocument, filePath);
+ var compilationFailure = new CompilationFailure(
+ filePath,
+ fileContent,
+ compiledContent: string.Empty,
+ messages: group.Select(parserError => CreateDiagnosticMessage(parserError, filePath)));
+ failures.Add(compilationFailure);
+ }
+
+ return new CompilationResult(failures);
+ }
+
+ private static string ReadContent(RazorCodeDocument codeDocument, string filePath)
+ {
+ RazorSourceDocument sourceDocument = null;
+ if (string.IsNullOrEmpty(filePath) || string.Equals(codeDocument.Source.FileName, filePath, StringComparison.Ordinal))
+ {
+ sourceDocument = codeDocument.Source;
+ }
+ else
+ {
+ sourceDocument = codeDocument.Imports.FirstOrDefault(f => string.Equals(f.FileName, filePath, StringComparison.Ordinal));
+ }
+
+ if (sourceDocument != null)
+ {
+ var contentChars = new char[sourceDocument.Length];
+ sourceDocument.CopyTo(0, contentChars, 0, sourceDocument.Length);
+ return new string(contentChars);
+ }
+
+ return string.Empty;
+ }
+
+ private static DiagnosticMessage CreateDiagnosticMessage(
+ RazorDiagnostic razorDiagnostic,
+ string filePath)
+ {
+ var sourceSpan = razorDiagnostic.Span;
+ var message = razorDiagnostic.GetMessage();
+ return new DiagnosticMessage(
+ message: message,
+ formattedMessage: razorDiagnostic.ToString(),
+ filePath: filePath,
+ startLine: sourceSpan.LineIndex + 1,
+ startColumn: sourceSpan.CharacterIndex,
+ endLine: sourceSpan.LineIndex + 1,
+ endColumn: sourceSpan.CharacterIndex + sourceSpan.Length);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Mvc.Razor/Properties/Resources.Designer.cs
index b036a6d5bd..e1bf657ac1 100644
--- a/src/Microsoft.AspNetCore.Mvc.Razor/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Razor/Properties/Resources.Designer.cs
@@ -58,22 +58,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor
return string.Format(CultureInfo.CurrentCulture, GetString("FlushPointCannotBeInvoked"), p0);
}
- ///
- /// The {0} returned by '{1}' must be an instance of '{2}'.
- ///
- internal static string Instrumentation_WriterMustBeBufferedTextWriter
- {
- get { return GetString("Instrumentation_WriterMustBeBufferedTextWriter"); }
- }
-
- ///
- /// The {0} returned by '{1}' must be an instance of '{2}'.
- ///
- internal static string FormatInstrumentation_WriterMustBeBufferedTextWriter(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("Instrumentation_WriterMustBeBufferedTextWriter"), p0, p1, p2);
- }
-
///
/// The layout view '{0}' could not be located. The following locations were searched:{1}
///
@@ -314,70 +298,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor
return string.Format(CultureInfo.CurrentCulture, GetString("ViewContextMustBeSet"), p0, p1);
}
- ///
- /// '{0}' must be a {1} that is generated as result of the call to '{2}'.
- ///
- internal static string ViewLocationCache_KeyMustBeString
- {
- get { return GetString("ViewLocationCache_KeyMustBeString"); }
- }
-
- ///
- /// '{0}' must be a {1} that is generated as result of the call to '{2}'.
- ///
- internal static string FormatViewLocationCache_KeyMustBeString(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ViewLocationCache_KeyMustBeString"), p0, p1, p2);
- }
-
- ///
- /// The '{0}' method must be called before '{1}' can be invoked.
- ///
- internal static string ViewMustBeContextualized
- {
- get { return GetString("ViewMustBeContextualized"); }
- }
-
- ///
- /// The '{0}' method must be called before '{1}' can be invoked.
- ///
- internal static string FormatViewMustBeContextualized(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ViewMustBeContextualized"), p0, p1);
- }
-
- ///
- /// Unsupported hash algorithm.
- ///
- internal static string RazorHash_UnsupportedHashAlgorithm
- {
- get { return GetString("RazorHash_UnsupportedHashAlgorithm"); }
- }
-
- ///
- /// Unsupported hash algorithm.
- ///
- internal static string FormatRazorHash_UnsupportedHashAlgorithm()
- {
- return GetString("RazorHash_UnsupportedHashAlgorithm");
- }
-
- ///
- /// The resource '{0}' specified by '{1}' could not be found.
- ///
- internal static string RazorFileInfoCollection_ResourceCouldNotBeFound
- {
- get { return GetString("RazorFileInfoCollection_ResourceCouldNotBeFound"); }
- }
-
- ///
- /// The resource '{0}' specified by '{1}' could not be found.
- ///
- internal static string FormatRazorFileInfoCollection_ResourceCouldNotBeFound(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("RazorFileInfoCollection_ResourceCouldNotBeFound"), p0, p1);
- }
-
///
/// Generated Code
///
@@ -526,6 +446,22 @@ namespace Microsoft.AspNetCore.Mvc.Razor
return GetString("RazorProject_PathMustStartWithForwardSlash");
}
+ ///
+ /// The property '{0}' of '{1}' must not be null.
+ ///
+ internal static string PropertyMustBeSet
+ {
+ get { return GetString("PropertyMustBeSet"); }
+ }
+
+ ///
+ /// The property '{0}' of '{1}' must not be null.
+ ///
+ internal static string FormatPropertyMustBeSet(object p0, object p1)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("PropertyMustBeSet"), p0, p1);
+ }
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngine.cs b/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngine.cs
index 3509690f3c..b5f00e46b3 100644
--- a/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngine.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Razor/RazorViewEngine.cs
@@ -11,6 +11,7 @@ using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Mvc.ViewEngines;
+using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -30,6 +31,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
public class RazorViewEngine : IRazorViewEngine
{
public static readonly string ViewExtension = ".cshtml";
+ private const string ViewStartFileName = "_ViewStart.cshtml";
private const string ControllerKey = "controller";
private const string AreaKey = "area";
@@ -42,6 +44,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
private readonly HtmlEncoder _htmlEncoder;
private readonly ILogger _logger;
private readonly RazorViewEngineOptions _options;
+ private readonly RazorProject _razorProject;
///
/// Initializes a new instance of the .
@@ -51,6 +54,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
IRazorPageActivator pageActivator,
HtmlEncoder htmlEncoder,
IOptions optionsAccessor,
+ RazorProject razorProject,
ILoggerFactory loggerFactory)
{
_options = optionsAccessor.Value;
@@ -73,6 +77,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
_pageActivator = pageActivator;
_htmlEncoder = htmlEncoder;
_logger = loggerFactory.CreateLogger();
+ _razorProject = razorProject;
ViewLookupCache = new MemoryCache(new MemoryCacheOptions
{
CompactOnMemoryPressure = false
@@ -483,10 +488,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor
string path,
HashSet expirationTokens)
{
+ var applicationRelativePath = MakePathApplicationRelative(path);
var viewStartPages = new List();
- foreach (var viewStartPath in ViewHierarchyUtility.GetViewStartLocations(path))
+
+ foreach (var viewStartProjectItem in _razorProject.FindHierarchicalItems(applicationRelativePath, ViewStartFileName))
{
- var result = _pageFactory.CreateFactory(viewStartPath);
+ var result = _pageFactory.CreateFactory(viewStartProjectItem.Path);
if (result.ExpirationTokens != null)
{
for (var i = 0; i < result.ExpirationTokens.Count; i++)
@@ -500,7 +507,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
// Populate the viewStartPages list so that _ViewStarts appear in the order the need to be
// executed (closest last, furthest first). This is the reverse order in which
// ViewHierarchyUtility.GetViewStartLocations returns _ViewStarts.
- viewStartPages.Insert(0, new ViewLocationCacheItem(result.RazorPageFactory, viewStartPath));
+ viewStartPages.Insert(0, new ViewLocationCacheItem(result.RazorPageFactory, viewStartProjectItem.Path));
}
}
@@ -533,6 +540,22 @@ namespace Microsoft.AspNetCore.Mvc.Razor
return name[0] == '~' || name[0] == '/';
}
+ private string MakePathApplicationRelative(string path)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(path));
+ if (path[0] == '~')
+ {
+ path = path.Substring(1);
+ }
+
+ if (path[0] != '/')
+ {
+ path = '/' + path;
+ }
+
+ return path;
+ }
+
private static bool IsRelativePath(string name)
{
Debug.Assert(!string.IsNullOrEmpty(name));
diff --git a/src/Microsoft.AspNetCore.Mvc.Razor/Resources.resx b/src/Microsoft.AspNetCore.Mvc.Razor/Resources.resx
index 44bc4d7d75..edcc139e30 100644
--- a/src/Microsoft.AspNetCore.Mvc.Razor/Resources.resx
+++ b/src/Microsoft.AspNetCore.Mvc.Razor/Resources.resx
@@ -126,9 +126,6 @@
'{0}' cannot be invoked when a Layout page is set to be executed.
-
- The {0} returned by '{1}' must be an instance of '{2}'.
-
The layout view '{0}' could not be located. The following locations were searched:{1}
@@ -174,18 +171,6 @@
'{0} must be set to access '{1}'.
-
- '{0}' must be a {1} that is generated as result of the call to '{2}'.
-
-
- The '{0}' method must be called before '{1}' can be invoked.
-
-
- Unsupported hash algorithm.
-
-
- The resource '{0}' specified by '{1}' could not be found.
-
Generated Code
@@ -215,4 +200,7 @@
Path must begin with a forward slash '/'.
+
+ The property '{0}' of '{1}' must not be null.
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/IPageLoader.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/IPageLoader.cs
index 68d69634c2..5d9a9cc0c4 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/IPageLoader.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Infrastructure/IPageLoader.cs
@@ -1,9 +1,18 @@
-using System;
+// 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.
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
+ ///
+ /// Creates a from a .
+ ///
public interface IPageLoader
{
+ ///
+ /// Produces a given a .
+ ///
+ /// The .
+ /// The .
CompiledPageActionDescriptor Load(PageActionDescriptor actionDescriptor);
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageLoader.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageLoader.cs
index e50be6ad58..ed12fbfac3 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageLoader.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageLoader.cs
@@ -1,123 +1,51 @@
// 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 System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
using System.Reflection;
+using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.AspNetCore.Razor.Evolution;
-using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{
public class DefaultPageLoader : IPageLoader
{
+ private const string PageImportsFileName = "_PageImports.cshtml";
private const string ModelPropertyName = "Model";
- private readonly RazorCompilationService _razorCompilationService;
- private readonly ICompilationService _compilationService;
- private readonly RazorProject _project;
- private readonly ILogger _logger;
+
+ private readonly MvcRazorTemplateEngine _templateEngine;
+ private readonly RazorCompiler _razorCompiler;
public DefaultPageLoader(
- IRazorCompilationService razorCompilationService,
- ICompilationService compilationService,
+ RazorEngine razorEngine,
RazorProject razorProject,
- ILogger logger)
+ ICompilationService compilationService,
+ ICompilerCacheProvider compilerCacheProvider)
{
- _razorCompilationService = (RazorCompilationService)razorCompilationService;
- _compilationService = compilationService;
- _project = razorProject;
- _logger = logger;
+ _templateEngine = new MvcRazorTemplateEngine(razorEngine, razorProject);
+ _templateEngine.Options.ImportsFileName = PageImportsFileName;
+ _razorCompiler = new RazorCompiler(compilationService, compilerCacheProvider, _templateEngine);
}
public CompiledPageActionDescriptor Load(PageActionDescriptor actionDescriptor)
{
- var item = _project.GetItem(actionDescriptor.RelativePath);
- if (!item.Exists)
- {
- throw new InvalidOperationException($"File {actionDescriptor.RelativePath} was not found.");
- }
-
- _logger.RazorFileToCodeCompilationStart(item.Path);
-
- var startTimestamp = _logger.IsEnabled(LogLevel.Debug) ? Stopwatch.GetTimestamp() : 0;
-
- var codeDocument = CreateCodeDocument(item);
- var cSharpDocument = _razorCompilationService.ProcessCodeDocument(codeDocument);
-
- _logger.RazorFileToCodeCompilationEnd(item.Path, startTimestamp);
-
- CompilationResult compilationResult;
- if (cSharpDocument.Diagnostics.Count > 0)
- {
- compilationResult = _razorCompilationService.GetCompilationFailedResult(item.Path, cSharpDocument.Diagnostics);
- }
- else
- {
- compilationResult = _compilationService.Compile(codeDocument, cSharpDocument);
- }
-
- compilationResult.EnsureSuccessful();
-
+ var compilationResult = _razorCompiler.Compile(actionDescriptor.RelativePath);
+ var compiledTypeInfo = compilationResult.CompiledType.GetTypeInfo();
// If a model type wasn't set in code then the model property's type will be the same
// as the compiled type.
- var pageType = compilationResult.CompiledType.GetTypeInfo();
- var modelType = pageType.GetProperty(ModelPropertyName)?.PropertyType.GetTypeInfo();
- if (modelType == pageType)
+ var modelTypeInfo = compiledTypeInfo.GetProperty(ModelPropertyName)?.PropertyType.GetTypeInfo();
+ if (modelTypeInfo == compiledTypeInfo)
{
- modelType = null;
+ modelTypeInfo = null;
}
return new CompiledPageActionDescriptor(actionDescriptor)
{
- ModelTypeInfo = modelType,
- PageTypeInfo = pageType,
+ PageTypeInfo = compiledTypeInfo,
+ ModelTypeInfo = modelTypeInfo,
};
}
-
- private RazorCodeDocument CreateCodeDocument(RazorProjectItem item)
- {
- var absolutePath = GetItemPath(item);
-
- RazorSourceDocument source;
- using (var inputStream = item.Read())
- {
- source = RazorSourceDocument.ReadFrom(inputStream, absolutePath);
- }
-
- var imports = new List()
- {
- _razorCompilationService.GlobalImports,
- };
-
- var pageImports = _project.FindHierarchicalItems(item.Path, "_PageImports.cshtml");
- foreach (var pageImport in pageImports.Reverse())
- {
- if (pageImport.Exists)
- {
- using (var stream = pageImport.Read())
- {
- imports.Add(RazorSourceDocument.ReadFrom(stream, GetItemPath(item)));
- }
- }
- }
-
- return RazorCodeDocument.Create(source, imports);
- }
-
- private static string GetItemPath(RazorProjectItem item)
- {
- var absolutePath = item.Path;
- if (item.Exists && string.IsNullOrEmpty(item.PhysicalPath))
- {
- absolutePath = item.PhysicalPath;
- }
-
- return absolutePath;
- }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/MvcRazorTemplateEngineTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/MvcRazorTemplateEngineTest.cs
new file mode 100644
index 0000000000..b55ef321a6
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/MvcRazorTemplateEngineTest.cs
@@ -0,0 +1,123 @@
+// 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 System.Linq;
+using Microsoft.AspNetCore.Mvc.Razor.Internal;
+using Microsoft.AspNetCore.Razor.Evolution;
+using Microsoft.Extensions.FileProviders;
+using Moq;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Mvc.Razor.Host
+{
+ public class MvcRazorTemplateEngineTest
+ {
+ [Fact]
+ public void GetDefaultImports_IncludesDefaultImports()
+ {
+ // Arrange
+ var expectedImports = new[]
+ {
+ "@using System",
+ "@using System.Linq",
+ "@using System.Collections.Generic",
+ "@using Microsoft.AspNetCore.Mvc",
+ "@using Microsoft.AspNetCore.Mvc.Rendering",
+ "@using Microsoft.AspNetCore.Mvc.ViewFeatures",
+ };
+ var mvcRazorTemplateEngine = new MvcRazorTemplateEngine(
+ RazorEngine.Create(),
+ GetRazorProject(new TestFileProvider()));
+
+ // Act
+ var imports = mvcRazorTemplateEngine.Options.DefaultImports;
+
+ // Assert
+ var importContent = GetContent(imports)
+ .Split(new[] { Environment.NewLine }, StringSplitOptions.None)
+ .Where(line => line.StartsWith("@using"));
+ Assert.Equal(expectedImports, importContent);
+ }
+
+ [Fact]
+ public void GetDefaultImports_IncludesDefaulInjects()
+ {
+ // Arrange
+ var expectedImports = new[]
+ {
+ "@inject global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper Html",
+ "@inject global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json",
+ "@inject global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component",
+ "@inject global::Microsoft.AspNetCore.Mvc.IUrlHelper Url",
+ "@inject global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider",
+ };
+ var mvcRazorTemplateEngine = new MvcRazorTemplateEngine(
+ RazorEngine.Create(),
+ GetRazorProject(new TestFileProvider()));
+
+ // Act
+ var imports = mvcRazorTemplateEngine.Options.DefaultImports;
+
+ // Assert
+ var importContent = GetContent(imports)
+ .Split(new[] { Environment.NewLine }, StringSplitOptions.None)
+ .Where(line => line.StartsWith("@inject"));
+ Assert.Equal(expectedImports, importContent);
+ }
+
+ [Fact]
+ public void GetDefaultImports_IncludesUrlTagHelper()
+ {
+ // Arrange
+ var mvcRazorTemplateEngine = new MvcRazorTemplateEngine(
+ RazorEngine.Create(),
+ GetRazorProject(new TestFileProvider()));
+
+ // Act
+ var imports = mvcRazorTemplateEngine.Options.DefaultImports;
+
+ // Assert
+ var importContent = GetContent(imports)
+ .Split(new[] { Environment.NewLine }, StringSplitOptions.None)
+ .Where(line => line.StartsWith("@addTagHelper"));
+ var addTagHelper = Assert.Single(importContent);
+ Assert.Equal("@addTagHelper Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper, Microsoft.AspNetCore.Mvc.Razor",
+ addTagHelper);
+ }
+
+ [Fact]
+ public void CreateCodeDocument_SetsRelativePathOnOutput()
+ {
+ // Arrange
+ var path = "/Views/Home/Index.cshtml";
+ var fileProvider = new TestFileProvider();
+ fileProvider.AddFile(path, "Hello world");
+ var mvcRazorTemplateEngine = new MvcRazorTemplateEngine(
+ RazorEngine.Create(),
+ GetRazorProject(fileProvider));
+
+ // Act
+ var codeDocument = mvcRazorTemplateEngine.CreateCodeDocument(path);
+
+ // Assert
+ Assert.Equal(path, codeDocument.GetRelativePath());
+ }
+
+ private string GetContent(RazorSourceDocument imports)
+ {
+ var contentChars = new char[imports.Length];
+ imports.CopyTo(0, contentChars, 0, imports.Length);
+ return new string(contentChars);
+ }
+
+ private static DefaultRazorProject GetRazorProject(IFileProvider fileProvider)
+ {
+ var fileProviderAccessor = new Mock();
+ fileProviderAccessor.SetupGet(f => f.FileProvider)
+ .Returns(fileProvider);
+
+ return new DefaultRazorProject(fileProviderAccessor.Object);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/ViewHierarchyUtilityTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/ViewHierarchyUtilityTest.cs
deleted file mode 100644
index 86159c234d..0000000000
--- a/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/ViewHierarchyUtilityTest.cs
+++ /dev/null
@@ -1,277 +0,0 @@
-// 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.IO;
-using Microsoft.AspNetCore.Testing.xunit;
-using Xunit;
-
-namespace Microsoft.AspNetCore.Mvc.Razor
-{
- public class ViewHierarchyUtilityTest
- {
- [Theory]
- [InlineData(null)]
- [InlineData("")]
- public void GetViewStartLocations_ReturnsEmptySequenceIfViewPathIsEmpty(string viewPath)
- {
- // Act
- var result = ViewHierarchyUtility.GetViewStartLocations(viewPath);
-
- // Assert
- Assert.Empty(result);
- }
-
- [Theory]
- [InlineData(null)]
- [InlineData("")]
- public void GetViewImportsLocations_ReturnsEmptySequenceIfViewPathIsEmpty(string viewPath)
- {
- // Act
- var result = ViewHierarchyUtility.GetViewImportsLocations(viewPath);
-
- // Assert
- Assert.Empty(result);
- }
-
- [Theory]
- [InlineData("/Views/Home/MyView.cshtml")]
- [InlineData("~/Views/Home/MyView.cshtml")]
- [InlineData("Views/Home/MyView.cshtml")]
- public void GetViewStartLocations_ReturnsPotentialViewStartLocations_PathStartswithSlash(string inputPath)
- {
- // Arrange
- var expected = new[]
- {
- "/Views/Home/_ViewStart.cshtml",
- "/Views/_ViewStart.cshtml",
- "/_ViewStart.cshtml"
- };
-
- // Act
- var result = ViewHierarchyUtility.GetViewStartLocations(inputPath);
-
- // Assert
- Assert.Equal(expected, result);
- }
-
- [ConditionalTheory]
- [OSSkipCondition(OperatingSystems.Linux,
- SkipReason = "Back slashes only work as path separators on Windows")]
- [OSSkipCondition(OperatingSystems.MacOSX,
- SkipReason = "Back slashes only work as path separators on Windows")]
- [InlineData(@"~/Views\Home\MyView.cshtml")]
- [InlineData(@"Views\Home\MyView.cshtml")]
- public void GetViewStartLocations_ReturnsPotentialViewStartLocations_PathsContainBackSlash(
- string inputPath)
- {
- // Arrange
- var expected = new[]
- {
- "/Views/Home/_ViewStart.cshtml",
- "/Views/_ViewStart.cshtml",
- "/_ViewStart.cshtml"
- };
-
- // Act
- var result = ViewHierarchyUtility.GetViewStartLocations(inputPath);
-
- // Assert
- Assert.Equal(expected, result);
- }
-
- [Theory]
- [InlineData("/Views/Home/MyView.cshtml")]
- [InlineData("~/Views/Home/MyView.cshtml")]
- [InlineData("Views/Home/MyView.cshtml")]
- public void GetViewImportsLocations_ReturnsPotentialViewStartLocations_PathStartswithSlash(string inputPath)
- {
- // Arrange
- var expected = new[]
- {
- "/Views/Home/_ViewImports.cshtml",
- "/Views/_ViewImports.cshtml",
- "/_ViewImports.cshtml"
- };
-
- // Act
- var result = ViewHierarchyUtility.GetViewImportsLocations(inputPath);
-
- // Assert
- Assert.Equal(expected, result);
- }
-
- [Theory]
- [InlineData("/Views/Home/_ViewStart.cshtml")]
- [InlineData("~/Views/Home/_ViewStart.cshtml")]
- [InlineData("Views/Home/_ViewStart.cshtml")]
- public void GetViewStartLocations_SkipsCurrentPath_IfCurrentIsViewStart(string inputPath)
- {
- // Arrange
- var expected = new[]
- {
- "/Views/_ViewStart.cshtml",
- "/_ViewStart.cshtml"
- };
-
- // Act
- var result = ViewHierarchyUtility.GetViewStartLocations(inputPath);
-
- // Assert
- Assert.Equal(expected, result);
- }
-
- [Theory]
- [InlineData("/Views/Home/_ViewStart.cshtml")]
- [InlineData("~/Views/Home/_ViewStart.cshtml")]
- [InlineData("Views/Home/_ViewStart.cshtml")]
- public void GetViewImportsLocations_WhenCurrentIsViewStart(string inputPath)
- {
- // Arrange
- var expected = new[]
- {
- "/Views/Home/_ViewImports.cshtml",
- "/Views/_ViewImports.cshtml",
- "/_ViewImports.cshtml"
- };
-
- // Act
- var result = ViewHierarchyUtility.GetViewImportsLocations(inputPath);
-
- // Assert
- Assert.Equal(expected, result);
- }
-
- [Theory]
- [InlineData("/Views/Home/_ViewImports.cshtml")]
- [InlineData("~/Views/Home/_ViewImports.cshtml")]
- [InlineData("Views/Home/_ViewImports.cshtml")]
- public void GetViewImportsLocations_SkipsCurrentPath_IfCurrentIsViewImports(string inputPath)
- {
- // Arrange
- var expected = new[]
- {
- "/Views/_ViewImports.cshtml",
- "/_ViewImports.cshtml"
- };
-
- // Act
- var result = ViewHierarchyUtility.GetViewImportsLocations(inputPath);
-
- // Assert
- Assert.Equal(expected, result);
- }
-
- [Theory]
- [InlineData("Test.cshtml")]
- [InlineData("ViewStart.cshtml")]
- public void GetViewStartLocations_ReturnsPotentialViewStartLocations(string fileName)
- {
- // Arrange
- var expected = new[]
- {
- "/Areas/MyArea/Sub/Views/Admin/_ViewStart.cshtml",
- "/Areas/MyArea/Sub/Views/_ViewStart.cshtml",
- "/Areas/MyArea/Sub/_ViewStart.cshtml",
- "/Areas/MyArea/_ViewStart.cshtml",
- "/Areas/_ViewStart.cshtml",
- "/_ViewStart.cshtml",
- };
- var viewPath = $"Areas/MyArea/Sub/Views/Admin/{fileName}";
-
- // Act
- var result = ViewHierarchyUtility.GetViewStartLocations(viewPath);
-
- // Assert
- Assert.Equal(expected, result);
- }
-
- [ConditionalTheory]
- [OSSkipCondition(OperatingSystems.Linux,
- SkipReason = "Back slashes only work as path separators on Windows")]
- [OSSkipCondition(OperatingSystems.MacOSX,
- SkipReason = "Back slashes only work as path separators on Windows")]
- [InlineData("Test.cshtml")]
- [InlineData("ViewStart.cshtml")]
- public void GetViewStartLocations_ReturnsPotentialViewStartLocations_ForPathsWithBackSlashes(string fileName)
- {
- // Arrange
- var expected = new[]
- {
- "/Areas/MyArea/Sub/Views/Admin/_ViewStart.cshtml",
- "/Areas/MyArea/Sub/Views/_ViewStart.cshtml",
- "/Areas/MyArea/Sub/_ViewStart.cshtml",
- "/Areas/MyArea/_ViewStart.cshtml",
- "/Areas/_ViewStart.cshtml",
- "/_ViewStart.cshtml",
- };
- var viewPath = $"Areas\\MyArea\\Sub\\Views\\Admin/{fileName}";
-
- // Act
- var result = ViewHierarchyUtility.GetViewStartLocations(viewPath);
-
- // Assert
- Assert.Equal(expected, result);
- }
-
- [Theory]
- [InlineData("Test.cshtml")]
- [InlineData("Global.cshtml")]
- [InlineData("_ViewStart.cshtml")]
- public void GetViewImportsLocations_ReturnsPotentialGlobalLocations(string fileName)
- {
- // Arrange
- var expected = new[]
- {
- "/Areas/MyArea/Sub/Views/Admin/_ViewImports.cshtml",
- "/Areas/MyArea/Sub/Views/_ViewImports.cshtml",
- "/Areas/MyArea/Sub/_ViewImports.cshtml",
- "/Areas/MyArea/_ViewImports.cshtml",
- "/Areas/_ViewImports.cshtml",
- "/_ViewImports.cshtml",
- };
- var viewPath = $"Areas/MyArea/Sub/Views/Admin/{fileName}";
-
- // Act
- var result = ViewHierarchyUtility.GetViewImportsLocations(viewPath);
-
- // Assert
- Assert.Equal(expected, result);
- }
-
- [Theory]
- [InlineData("_ViewStart.cshtml")]
- [InlineData("_viewstart.cshtml")]
- public void GetViewStartLocations_SkipsCurrentPath_IfPathIsAViewStartFile(string fileName)
- {
- // Arrange
- var expected = new[]
- {
- "/Areas/MyArea/Sub/Views/_ViewStart.cshtml",
- "/Areas/MyArea/Sub/_ViewStart.cshtml",
- "/Areas/MyArea/_ViewStart.cshtml",
- "/Areas/_ViewStart.cshtml",
- "/_ViewStart.cshtml",
- };
- var viewPath = $"Areas/MyArea/Sub/Views/Admin/{fileName}";
-
- // Act
- var result = ViewHierarchyUtility.GetViewStartLocations(viewPath);
-
- // Assert
- Assert.Equal(expected, result);
- }
-
- [Fact]
- public void GetViewStartLocations_ReturnsEmptySequence_IfViewStartIsAtRoot()
- {
- // Arrange
- var viewPath = "_ViewStart.cshtml";
-
- // Act
- var result = ViewHierarchyUtility.GetViewStartLocations(viewPath);
-
- // Assert
- Assert.Empty(result);
- }
- }
-}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/CompilerCacheTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/CompilerCacheTest.cs
index 5e5e4885b1..cf9f9f4296 100644
--- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/CompilerCacheTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/CompilerCacheTest.cs
@@ -3,10 +3,12 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
+using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.Extensions.FileProviders;
using Moq;
using Xunit;
@@ -17,18 +19,30 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
{
private const string ViewPath = "/Views/Home/Index.cshtml";
private const string PrecompiledViewsPath = "/Views/Home/Precompiled.cshtml";
+ private static readonly string[] _viewImportsPath = new[]
+ {
+ "/Views/Home/_ViewImports.cshtml",
+ "/Views/_ViewImports.cshtml",
+ "/_ViewImports.cshtml",
+ };
private readonly IDictionary _precompiledViews = new Dictionary
{
{ PrecompiledViewsPath, typeof(PreCompile) }
};
- public static TheoryData ViewImportsPaths =>
- new TheoryData
+ public static TheoryData ViewImportsPaths
+ {
+ get
{
- "/Views/Home/_ViewImports.cshtml",
- "/Views/_ViewImports.cshtml",
- "/_ViewImports.cshtml",
- };
+ var theoryData = new TheoryData();
+ foreach (var path in _viewImportsPath)
+ {
+ theoryData.Add(path);
+ }
+
+ return theoryData;
+ }
+ }
[Fact]
public void GetOrAdd_ReturnsFileNotFoundResult_IfFileIsNotFoundInFileSystem()
@@ -36,9 +50,13 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// Arrange
var fileProvider = new TestFileProvider();
var cache = new CompilerCache(fileProvider);
+ var compilerCacheContext = new CompilerCacheContext(
+ new NotFoundProjectItem("", "/path"),
+ Enumerable.Empty(),
+ _ => throw new Exception("Shouldn't be called."));
// Act
- var result = cache.GetOrAdd("/some/path", ThrowsIfCalled);
+ var result = cache.GetOrAdd("/some/path", _ => compilerCacheContext);
// Assert
Assert.False(result.Success);
@@ -54,12 +72,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var expected = new CompilationResult(typeof(TestView));
// Act
- var result = cache.GetOrAdd(ViewPath, _ => expected);
+ var result = cache.GetOrAdd(ViewPath, CreateContextFactory(expected));
// Assert
Assert.True(result.Success);
- Assert.IsType(result.PageFactory());
- Assert.Same(ViewPath, result.PageFactory().Path);
+ Assert.Equal(typeof(TestView), result.CompiledType);
+ Assert.Equal(ViewPath, result.RelativePath);
}
[Theory]
@@ -77,17 +95,16 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var expected = new CompilationResult(typeof(TestView));
// Act - 1
- var result1 = cache.GetOrAdd(@"Areas\Finances\Views\Home\Index.cshtml", _ => expected);
+ var result1 = cache.GetOrAdd(@"Areas\Finances\Views\Home\Index.cshtml", CreateContextFactory(expected));
// Assert - 1
- Assert.IsType(result1.PageFactory());
+ Assert.Equal(typeof(TestView), result1.CompiledType);
// Act - 2
var result2 = cache.GetOrAdd(relativePath, ThrowsIfCalled);
// Assert - 2
- Assert.IsType(result2.PageFactory());
- Assert.Same(result1.PageFactory, result2.PageFactory);
+ Assert.Equal(typeof(TestView), result2.CompiledType);
}
[Fact]
@@ -95,22 +112,27 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
{
// Arrange
var fileProvider = new TestFileProvider();
- fileProvider.AddFile(ViewPath, "some content");
+ var fileInfo = fileProvider.AddFile(ViewPath, "some content");
var cache = new CompilerCache(fileProvider);
var expected = new CompilationResult(typeof(TestView));
+ var projectItem = new DefaultRazorProjectItem(fileInfo, "", ViewPath);
+ var cacheContext = new CompilerCacheContext(projectItem, Enumerable.Empty(), _ => expected);
// Act 1
- var result1 = cache.GetOrAdd(ViewPath, _ => expected);
+ var result1 = cache.GetOrAdd(ViewPath, _ => cacheContext);
// Assert 1
Assert.True(result1.Success);
- Assert.IsType(result1.PageFactory());
+ Assert.Equal(typeof(TestView), result1.CompiledType);
// Act 2
// Delete the file from the file system and set it's expiration token.
- fileProvider.DeleteFile(ViewPath);
+ cacheContext = new CompilerCacheContext(
+ new NotFoundProjectItem("", ViewPath),
+ Enumerable.Empty(),
+ _ => throw new Exception("Shouldn't be called."));
fileProvider.GetChangeToken(ViewPath).HasChanged = true;
- var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
+ var result2 = cache.GetOrAdd(ViewPath, _ => cacheContext);
// Assert 2
Assert.False(result2.Success);
@@ -127,11 +149,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var expected2 = new CompilationResult(typeof(DifferentView));
// Act 1
- var result1 = cache.GetOrAdd(ViewPath, _ => expected1);
+ var result1 = cache.GetOrAdd(ViewPath, CreateContextFactory(expected1));
// Assert 1
Assert.True(result1.Success);
- Assert.IsType(result1.PageFactory());
+ Assert.Equal(typeof(TestView), result1.CompiledType);
// Act 2
// Verify we're getting cached results.
@@ -139,15 +161,15 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// Assert 2
Assert.True(result2.Success);
- Assert.IsType(result2.PageFactory());
+ Assert.Equal(typeof(TestView), result1.CompiledType);
// Act 3
fileProvider.GetChangeToken(ViewPath).HasChanged = true;
- var result3 = cache.GetOrAdd(ViewPath, _ => expected2);
+ var result3 = cache.GetOrAdd(ViewPath, CreateContextFactory(expected2));
// Assert 3
Assert.True(result3.Success);
- Assert.IsType(result3.PageFactory());
+ Assert.Equal(typeof(DifferentView), result3.CompiledType);
}
[Theory]
@@ -162,11 +184,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var expected2 = new CompilationResult(typeof(DifferentView));
// Act 1
- var result1 = cache.GetOrAdd(ViewPath, _ => expected1);
+ var result1 = cache.GetOrAdd(ViewPath, CreateContextFactory(expected1));
// Assert 1
Assert.True(result1.Success);
- Assert.IsType(result1.PageFactory());
+ Assert.Equal(typeof(TestView), result1.CompiledType);
// Act 2
// Verify we're getting cached results.
@@ -174,15 +196,15 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// Assert 2
Assert.True(result2.Success);
- Assert.IsType(result2.PageFactory());
+ Assert.Equal(typeof(TestView), result1.CompiledType);
// Act 3
fileProvider.GetChangeToken(globalImportPath).HasChanged = true;
- var result3 = cache.GetOrAdd(ViewPath, _ => expected2);
+ var result3 = cache.GetOrAdd(ViewPath, CreateContextFactory(expected2));
// Assert 2
Assert.True(result3.Success);
- Assert.IsType(result3.PageFactory());
+ Assert.Equal(typeof(DifferentView), result3.CompiledType);
}
[Fact]
@@ -196,19 +218,17 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var expected = new CompilationResult(typeof(TestView));
// Act 1
- var result1 = cache.GetOrAdd(ViewPath, _ => expected);
+ var result1 = cache.GetOrAdd(ViewPath, CreateContextFactory(expected));
// Assert 1
Assert.True(result1.Success);
- Assert.IsType(result1.PageFactory());
+ Assert.Equal(typeof(TestView), result1.CompiledType);
// Act 2
var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
// Assert 2
Assert.True(result2.Success);
- Assert.IsType(result2.PageFactory());
- mockFileProvider.Verify(v => v.GetFileInfo(ViewPath), Times.Once());
}
[Fact]
@@ -223,8 +243,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// Assert
Assert.True(result.Success);
- Assert.IsType(result.PageFactory());
- Assert.Same(PrecompiledViewsPath, result.PageFactory().Path);
+ Assert.Equal(typeof(PreCompile), result.CompiledType);
+ Assert.Same(PrecompiledViewsPath, result.RelativePath);
}
[Fact]
@@ -242,7 +262,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// Assert
Assert.True(result.Success);
Assert.True(result.IsPrecompiled);
- Assert.IsType(result.PageFactory());
+ Assert.Equal(typeof(PreCompile), result.CompiledType);
}
[Theory]
@@ -260,11 +280,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// Assert
Assert.True(result.Success);
- Assert.IsType(result.PageFactory());
+ Assert.Equal(typeof(PreCompile), result.CompiledType);
}
[Fact]
- public void GetOrAdd_ReturnsRuntimeCompiledAndPrecompiledViews()
+ public void GetOrAdd_ReturnsRuntimeCompiled()
{
// Arrange
var fileProvider = new TestFileProvider();
@@ -273,24 +293,32 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var expected = new CompilationResult(typeof(TestView));
// Act 1
- var result1 = cache.GetOrAdd(ViewPath, _ => expected);
+ var result1 = cache.GetOrAdd(ViewPath, CreateContextFactory(expected));
// Assert 1
- Assert.IsType(result1.PageFactory());
+ Assert.Equal(typeof(TestView), result1.CompiledType);
// Act 2
var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
// Assert 2
Assert.True(result2.Success);
- Assert.IsType(result2.PageFactory());
+ Assert.Equal(typeof(TestView), result2.CompiledType);
+ }
- // Act 3
- var result3 = cache.GetOrAdd(PrecompiledViewsPath, ThrowsIfCalled);
+ [Fact]
+ public void GetOrAdd_ReturnsPrecompiledViews()
+ {
+ // Arrange
+ var fileProvider = new TestFileProvider();
+ var cache = new CompilerCache(fileProvider, _precompiledViews);
+ var expected = new CompilationResult(typeof(TestView));
- // Assert 3
- Assert.True(result2.Success);
- Assert.IsType(result3.PageFactory());
+ // Act
+ var result1 = cache.GetOrAdd(PrecompiledViewsPath, ThrowsIfCalled);
+
+ // Assert
+ Assert.Equal(typeof(PreCompile), result1.CompiledType);
}
[Theory]
@@ -314,8 +342,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var result = cache.GetOrAdd(relativePath, ThrowsIfCalled);
// Assert
- Assert.IsType(result.PageFactory());
- Assert.Same(viewPath, result.PageFactory().Path);
+ Assert.Equal(typeof(PreCompile), result.CompiledType);
+ Assert.Equal(viewPath, result.RelativePath);
}
[Theory]
@@ -337,7 +365,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var result = cache.GetOrAdd("/Areas/Finances/Views/Home/Index.cshtml", ThrowsIfCalled);
// Assert
- Assert.IsType(result.PageFactory());
+ Assert.Equal(typeof(PreCompile), result.CompiledType);
}
[Fact]
@@ -354,42 +382,55 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var compilingOne = false;
var compilingTwo = false;
+ Func compile1 = _ =>
+ {
+ compilingOne = true;
+
+ // Event 2
+ Assert.True(resetEvent1.WaitOne(waitDuration));
+
+ // Event 3
+ Assert.True(resetEvent2.Set());
+
+ // Event 6
+ Assert.True(resetEvent1.WaitOne(waitDuration));
+
+ Assert.True(compilingTwo);
+ return new CompilationResult(typeof(TestView));
+ };
+
+ Func compile2 = _ =>
+ {
+ compilingTwo = true;
+
+ // Event 4
+ Assert.True(resetEvent2.WaitOne(waitDuration));
+
+ // Event 5
+ Assert.True(resetEvent1.Set());
+
+ Assert.True(compilingOne);
+ return new CompilationResult(typeof(DifferentView));
+ };
+
+
// Act
var task1 = Task.Run(() =>
{
- return cache.GetOrAdd("/Views/Home/Index.cshtml", file =>
+ return cache.GetOrAdd("/Views/Home/Index.cshtml", path =>
{
- compilingOne = true;
-
- // Event 2
- resetEvent1.WaitOne(waitDuration);
-
- // Event 3
- resetEvent2.Set();
-
- // Event 6
- resetEvent1.WaitOne(waitDuration);
-
- Assert.True(compilingTwo);
- return new CompilationResult(typeof(TestView));
+ var projectItem = new DefaultRazorProjectItem(new TestFileInfo(), "", path);
+ return new CompilerCacheContext(projectItem, Enumerable.Empty(), compile1);
});
});
var task2 = Task.Run(() =>
{
// Event 4
- return cache.GetOrAdd("/Views/Home/About.cshtml", file =>
+ return cache.GetOrAdd("/Views/Home/About.cshtml", path =>
{
- compilingTwo = true;
-
- // Event 4
- resetEvent2.WaitOne(waitDuration);
-
- // Event 5
- resetEvent1.Set();
-
- Assert.True(compilingOne);
- return new CompilationResult(typeof(DifferentView));
+ var projectItem = new DefaultRazorProjectItem(new TestFileInfo(), "", path);
+ return new CompilerCacheContext(projectItem, Enumerable.Empty(), compile2);
});
});
@@ -416,24 +457,30 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var resetEvent2 = new ManualResetEvent(initialState: false);
var cache = new CompilerCache(fileProvider);
+ Func compile = _ =>
+ {
+ // Event 2
+ resetEvent1.WaitOne(waitDuration);
+
+ // Event 3
+ resetEvent2.Set();
+ return new CompilationResult(typeof(TestView));
+ };
+
// Act
var task1 = Task.Run(() =>
{
- return cache.GetOrAdd(ViewPath, file =>
+ return cache.GetOrAdd(ViewPath, path =>
{
- // Event 2
- resetEvent1.WaitOne(waitDuration);
-
- // Event 3
- resetEvent2.Set();
- return new CompilationResult(typeof(TestView));
+ var projectItem = new DefaultRazorProjectItem(new TestFileInfo(), "", path);
+ return new CompilerCacheContext(projectItem, Enumerable.Empty(), compile);
});
});
var task2 = Task.Run(() =>
{
// Event 4
- resetEvent2.WaitOne(waitDuration);
+ Assert.True(resetEvent2.WaitOne(waitDuration));
return cache.GetOrAdd(ViewPath, ThrowsIfCalled);
});
@@ -444,7 +491,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// Assert
var result1 = task1.Result;
var result2 = task2.Result;
- Assert.Same(result1.PageFactory, result2.PageFactory);
+ Assert.Same(result1.CompiledType, result2.CompiledType);
}
[Fact]
@@ -475,7 +522,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// Act and Assert - 1
var actual = Assert.Throws(() =>
- cache.GetOrAdd(ViewPath, _ => { throw exception; }));
+ cache.GetOrAdd(ViewPath, _ => ThrowsIfCalled(ViewPath, exception)));
Assert.Same(exception, actual);
// Act and Assert - 2
@@ -489,6 +536,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
// Arrange
var fileProvider = new TestFileProvider();
fileProvider.AddFile(ViewPath, "some content");
+ var changeToken = fileProvider.AddChangeToken(ViewPath);
var cache = new CompilerCache(fileProvider);
// Act and Assert - 1
@@ -496,11 +544,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
cache.GetOrAdd(ViewPath, _ => { throw new InvalidTimeZoneException(); }));
// Act - 2
- fileProvider.GetChangeToken(ViewPath).HasChanged = true;
- var result = cache.GetOrAdd(ViewPath, _ => new CompilationResult(typeof(TestView)));
+ changeToken.HasChanged = true;
+ var result = cache.GetOrAdd(ViewPath, CreateContextFactory(new CompilationResult(typeof(TestView))));
// Assert - 2
- Assert.IsType(result.PageFactory());
+ Assert.Same(typeof(TestView), result.CompiledType);
}
[Fact]
@@ -512,15 +560,16 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var cache = new CompilerCache(fileProvider);
var diagnosticMessages = new[]
{
- new AspNetCore.Diagnostics.DiagnosticMessage("message", "message", ViewPath, 1, 1, 1, 1)
+ new DiagnosticMessage("message", "message", ViewPath, 1, 1, 1, 1)
};
var compilationResult = new CompilationResult(new[]
{
new CompilationFailure(ViewPath, "some content", "compiled content", diagnosticMessages)
});
+ var context = CreateContextFactory(compilationResult);
// Act and Assert - 1
- var ex = Assert.Throws(() => cache.GetOrAdd(ViewPath, _ => compilationResult));
+ var ex = Assert.Throws(() => cache.GetOrAdd(ViewPath, context));
Assert.Same(compilationResult.CompilationFailures, ex.CompilationFailures);
// Act and Assert - 2
@@ -552,9 +601,38 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
}
}
- private CompilationResult ThrowsIfCalled(RelativeFileInfo file)
+ private CompilerCacheContext ThrowsIfCalled(string path) =>
+ ThrowsIfCalled(path, new Exception("Shouldn't be called"));
+
+ private CompilerCacheContext ThrowsIfCalled(string path, Exception exception)
{
- throw new Exception("Shouldn't be called");
+ exception = exception ?? new Exception("Shouldn't be called");
+ var projectItem = new DefaultRazorProjectItem(new TestFileInfo(), "", path);
+
+ return new CompilerCacheContext(
+ projectItem,
+ Enumerable.Empty(),
+ _ => throw exception);
+ }
+
+ private Func CreateContextFactory(CompilationResult compile)
+ {
+ return path => CreateCacheContext(compile, path);
+ }
+
+ private CompilerCacheContext CreateCacheContext(CompilationResult compile, string path = ViewPath)
+ {
+ var projectItem = new DefaultRazorProjectItem(new TestFileInfo(), "", path);
+
+ var imports = new List();
+ foreach (var importFilePath in _viewImportsPath)
+ {
+ var importProjectItem = new DefaultRazorProjectItem(new TestFileInfo(), "", importFilePath);
+
+ imports.Add(importProjectItem);
+ }
+
+ return new CompilerCacheContext(projectItem, imports, _ => compile);
}
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRazorPageFactoryProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRazorPageFactoryProviderTest.cs
index 326042190b..c31d8562f1 100644
--- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRazorPageFactoryProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/DefaultRazorPageFactoryProviderTest.cs
@@ -4,6 +4,7 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
+using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.Extensions.Primitives;
using Moq;
using Xunit;
@@ -16,6 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
public void CreateFactory_ReturnsExpirationTokensFromCompilerCache_ForUnsuccessfulResults()
{
// Arrange
+ var path = "/file-does-not-exist";
var expirationTokens = new[]
{
Mock.Of(),
@@ -23,18 +25,20 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
};
var compilerCache = new Mock();
compilerCache
- .Setup(f => f.GetOrAdd(It.IsAny(), It.IsAny>()))
- .Returns(new CompilerCacheResult(expirationTokens));
+ .Setup(f => f.GetOrAdd(It.IsAny(), It.IsAny>()))
+ .Returns(new CompilerCacheResult(path, expirationTokens));
var compilerCacheProvider = new Mock();
compilerCacheProvider
.SetupGet(c => c.Cache)
.Returns(compilerCache.Object);
var factoryProvider = new DefaultRazorPageFactoryProvider(
- Mock.Of(),
+ RazorEngine.Create(),
+ new DefaultRazorProject(new TestFileProvider()),
+ Mock.Of(),
compilerCacheProvider.Object);
// Act
- var result = factoryProvider.CreateFactory("/file-does-not-exist");
+ var result = factoryProvider.CreateFactory(path);
// Assert
Assert.False(result.Success);
@@ -53,14 +57,16 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
};
var compilerCache = new Mock();
compilerCache
- .Setup(f => f.GetOrAdd(It.IsAny(), It.IsAny>()))
+ .Setup(f => f.GetOrAdd(It.IsAny(), It.IsAny>()))
.Returns(new CompilerCacheResult(relativePath, new CompilationResult(typeof(TestRazorPage)), expirationTokens));
var compilerCacheProvider = new Mock();
compilerCacheProvider
.SetupGet(c => c.Cache)
.Returns(compilerCache.Object);
var factoryProvider = new DefaultRazorPageFactoryProvider(
- Mock.Of(),
+ RazorEngine.Create(),
+ new DefaultRazorProject(new TestFileProvider()),
+ Mock.Of(),
compilerCacheProvider.Object);
// Act
@@ -78,14 +84,16 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
var relativePath = "/file-exists";
var compilerCache = new Mock();
compilerCache
- .Setup(f => f.GetOrAdd(It.IsAny(), It.IsAny>()))
+ .Setup(f => f.GetOrAdd(It.IsAny(), It.IsAny>()))
.Returns(new CompilerCacheResult(relativePath, new CompilationResult(typeof(TestRazorPage)), new IChangeToken[0]));
var compilerCacheProvider = new Mock();
compilerCacheProvider
.SetupGet(c => c.Cache)
.Returns(compilerCache.Object);
var factoryProvider = new DefaultRazorPageFactoryProvider(
- Mock.Of(),
+ RazorEngine.Create(),
+ new DefaultRazorProject(new TestFileProvider()),
+ Mock.Of(),
compilerCacheProvider.Object);
// Act
diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/RazorCompilationServiceTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/RazorCompilationServiceTest.cs
deleted file mode 100644
index f7cc60b7c4..0000000000
--- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/RazorCompilationServiceTest.cs
+++ /dev/null
@@ -1,246 +0,0 @@
-// 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.Collections.Generic;
-using System.IO;
-using Microsoft.AspNetCore.Mvc.Razor.Compilation;
-using Microsoft.AspNetCore.Razor.Evolution;
-using Microsoft.Extensions.FileProviders;
-using Microsoft.Extensions.Logging.Testing;
-using Moq;
-using Xunit;
-
-namespace Microsoft.AspNetCore.Mvc.Razor.Internal
-{
- public class RazorCompilationServiceTest
- {
- [Fact]
- public void CompileCalculatesRootRelativePath()
- {
- // Arrange
- var viewPath = @"src\work\myapp\Views\index\home.cshtml";
- var relativePath = @"Views\index\home.cshtml";
-
- var fileInfo = new Mock();
- fileInfo.Setup(f => f.PhysicalPath).Returns(viewPath);
- fileInfo.Setup(f => f.CreateReadStream()).Returns(new MemoryStream(new byte[] { 0 }));
- var fileProvider = new TestFileProvider();
- fileProvider.AddFile(relativePath, fileInfo.Object);
- var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, relativePath);
-
- var compiler = new Mock();
- compiler.Setup(c => c.Compile(It.IsAny(), It.IsAny()))
- .Returns(new CompilationResult(typeof(RazorCompilationServiceTest)));
-
- var engine = new Mock();
- engine.Setup(e => e.Process(It.IsAny()))
- .Callback(document =>
- {
- document.SetCSharpDocument(new RazorCSharpDocument()
- {
- Diagnostics = new List()
- });
-
- Assert.Equal(viewPath, document.Source.FileName); // Assert if source file name is the root relative path
- }).Verifiable();
-
- var razorService = new RazorCompilationService(
- compiler.Object,
- engine.Object,
- new DefaultRazorProject(fileProvider),
- GetFileProviderAccessor(fileProvider),
- NullLoggerFactory.Instance);
-
- // Act
- razorService.Compile(relativeFileInfo);
-
- // Assert
- engine.Verify();
- }
-
- [Fact]
- public void Compile_ReturnsFailedResultIfParseFails()
- {
- // Arrange
- var relativePath = @"Views\index\home.cshtml";
- var fileInfo = new Mock();
- fileInfo.Setup(f => f.CreateReadStream()).Returns(new MemoryStream(new byte[] { 0 }));
- var fileProvider = new TestFileProvider();
- fileProvider.AddFile(relativePath, fileInfo.Object);
- var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, relativePath);
-
- var compiler = new Mock(MockBehavior.Strict);
-
- var engine = new Mock();
- engine.Setup(e => e.Process(It.IsAny()))
- .Callback(document =>
- {
- document.SetCSharpDocument(new RazorCSharpDocument()
- {
- Diagnostics = new List()
- {
- GetRazorDiagnostic("some message", new SourceLocation(1, 1, 1), length: 1)
- }
- });
- }).Verifiable();
-
- var razorService = new RazorCompilationService(
- compiler.Object,
- engine.Object,
- new DefaultRazorProject(fileProvider),
- GetFileProviderAccessor(fileProvider),
- NullLoggerFactory.Instance);
-
- // Act
- var result = razorService.Compile(relativeFileInfo);
-
- // Assert
- Assert.NotNull(result.CompilationFailures);
- Assert.Collection(result.CompilationFailures,
- failure =>
- {
- var message = Assert.Single(failure.Messages);
- Assert.Equal("some message", message.Message);
- });
- engine.Verify();
- }
-
- [Fact]
- public void Compile_ReturnsResultFromCompilationServiceIfParseSucceeds()
- {
- var relativePath = @"Views\index\home.cshtml";
- var fileInfo = new Mock();
- fileInfo.Setup(f => f.CreateReadStream()).Returns(new MemoryStream(new byte[] { 0 }));
- var fileProvider = new TestFileProvider();
- fileProvider.AddFile(relativePath, fileInfo.Object);
- var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, relativePath);
-
- var compilationResult = new CompilationResult(typeof(object));
- var compiler = new Mock();
- compiler.Setup(c => c.Compile(It.IsAny(), It.IsAny()))
- .Returns(compilationResult)
- .Verifiable();
-
- var engine = new Mock();
- engine.Setup(e => e.Process(It.IsAny()))
- .Callback(document =>
- {
- document.SetCSharpDocument(new RazorCSharpDocument()
- {
- Diagnostics = new List()
- });
- });
-
- var razorService = new RazorCompilationService(
- compiler.Object,
- engine.Object,
- new DefaultRazorProject(fileProvider),
- GetFileProviderAccessor(fileProvider),
- NullLoggerFactory.Instance);
-
- // Act
- var result = razorService.Compile(relativeFileInfo);
-
- // Assert
- Assert.Same(compilationResult.CompiledType, result.CompiledType);
- compiler.Verify();
- }
-
- [Fact]
- public void GetCompilationFailedResult_ReturnsCompilationResult_WithGroupedMessages()
- {
- // Arrange
- var viewPath = @"views/index.razor";
- var viewImportsPath = @"views/global.import.cshtml";
-
- var fileProvider = new TestFileProvider();
- var file = fileProvider.AddFile(viewPath, "View Content");
- fileProvider.AddFile(viewImportsPath, "Global Import Content");
- var razorService = new RazorCompilationService(
- Mock.Of(),
- Mock.Of(),
- new DefaultRazorProject(fileProvider),
- GetFileProviderAccessor(fileProvider),
- NullLoggerFactory.Instance);
- var errors = new[]
- {
- GetRazorDiagnostic("message-1", new SourceLocation(1, 2, 17), length: 1),
- GetRazorDiagnostic("message-2", new SourceLocation(viewPath, 1, 4, 6), length: 7),
- GetRazorDiagnostic("message-3", SourceLocation.Undefined, length: -1),
- GetRazorDiagnostic("message-4", new SourceLocation(viewImportsPath, 1, 3, 8), length: 4),
- };
-
- // Act
- var result = razorService.GetCompilationFailedResult(viewPath, errors);
-
- // Assert
- Assert.NotNull(result.CompilationFailures);
- Assert.Collection(result.CompilationFailures,
- failure =>
- {
- Assert.Equal(viewPath, failure.SourceFilePath);
- Assert.Equal("View Content", failure.SourceFileContent);
- Assert.Collection(failure.Messages,
- message =>
- {
- Assert.Equal(errors[0].GetMessage(), message.Message);
- Assert.Equal(viewPath, message.SourceFilePath);
- Assert.Equal(3, message.StartLine);
- Assert.Equal(17, message.StartColumn);
- Assert.Equal(3, message.EndLine);
- Assert.Equal(18, message.EndColumn);
- },
- message =>
- {
- Assert.Equal(errors[1].GetMessage(), message.Message);
- Assert.Equal(viewPath, message.SourceFilePath);
- Assert.Equal(5, message.StartLine);
- Assert.Equal(6, message.StartColumn);
- Assert.Equal(5, message.EndLine);
- Assert.Equal(13, message.EndColumn);
- },
- message =>
- {
- Assert.Equal(errors[2].GetMessage(), message.Message);
- Assert.Equal(viewPath, message.SourceFilePath);
- Assert.Equal(0, message.StartLine);
- Assert.Equal(-1, message.StartColumn);
- Assert.Equal(0, message.EndLine);
- Assert.Equal(-2, message.EndColumn);
- });
- },
- failure =>
- {
- Assert.Equal(viewImportsPath, failure.SourceFilePath);
- Assert.Equal("Global Import Content", failure.SourceFileContent);
- Assert.Collection(failure.Messages,
- message =>
- {
- Assert.Equal(errors[3].GetMessage(), message.Message);
- Assert.Equal(viewImportsPath, message.SourceFilePath);
- Assert.Equal(4, message.StartLine);
- Assert.Equal(8, message.StartColumn);
- Assert.Equal(4, message.EndLine);
- Assert.Equal(12, message.EndColumn);
- });
- });
- }
-
- private static IRazorViewEngineFileProviderAccessor GetFileProviderAccessor(IFileProvider fileProvider = null)
- {
- var options = new Mock();
- options.SetupGet(o => o.FileProvider)
- .Returns(fileProvider ?? new TestFileProvider());
-
- return options.Object;
- }
-
- private static RazorDiagnostic GetRazorDiagnostic(string message, SourceLocation sourceLocation, int length)
- {
- var diagnosticDescriptor = new RazorDiagnosticDescriptor("test-id", () => message, RazorDiagnosticSeverity.Error);
- var sourceSpan = new SourceSpan(sourceLocation, length);
-
- return RazorDiagnostic.Create(diagnosticDescriptor, sourceSpan);
- }
- }
-}
diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/RazorCompilerTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/RazorCompilerTest.cs
new file mode 100644
index 0000000000..764dbfd823
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/Internal/RazorCompilerTest.cs
@@ -0,0 +1,262 @@
+// 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.IO;
+using System.Text;
+using Microsoft.AspNetCore.Mvc.Razor.Compilation;
+using Microsoft.AspNetCore.Razor.Evolution;
+using Moq;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Mvc.Razor.Internal
+{
+ public class RazorCompilerTest
+ {
+ [Fact]
+ public void GetCompilationFailedResult_ReadsRazorErrorsFromPage()
+ {
+ // Arrange
+ var viewPath = "/Views/Home/Index.cshtml";
+ var razorEngine = RazorEngine.Create();
+ var fileProvider = new TestFileProvider();
+ fileProvider.AddFile(viewPath, "");
+ var razorProject = new DefaultRazorProject(fileProvider);
+
+ var templateEngine = new MvcRazorTemplateEngine(razorEngine, razorProject);
+ var compiler = new RazorCompiler(
+ Mock.Of(),
+ GetCompilerCacheProvider(fileProvider),
+ templateEngine);
+ var codeDocument = templateEngine.CreateCodeDocument(viewPath);
+
+ // Act
+ var csharpDocument = templateEngine.GenerateCode(codeDocument);
+ var compilationResult = compiler.GetCompilationFailedResult(codeDocument, csharpDocument.Diagnostics);
+
+ // Assert
+ var failure = Assert.Single(compilationResult.CompilationFailures);
+ Assert.Equal(viewPath, failure.SourceFilePath);
+ Assert.Collection(failure.Messages,
+ message => Assert.StartsWith(
+ @"(1,22): Error RZ9999: Unterminated string literal.",
+ message.FormattedMessage),
+ message => Assert.StartsWith(
+ @"(1,14): Error RZ9999: The explicit expression block is missing a closing "")"" character.",
+ message.FormattedMessage));
+ }
+
+ [Fact]
+ public void GetCompilationFailedResult_UsesPhysicalPath()
+ {
+ // Arrange
+ var viewPath = "/Views/Home/Index.cshtml";
+ var physicalPath = @"x:\myapp\views\home\index.cshtml";
+ var razorEngine = RazorEngine.Create();
+ var fileProvider = new TestFileProvider();
+ var file = fileProvider.AddFile(viewPath, "");
+ file.PhysicalPath = physicalPath;
+ var razorProject = new DefaultRazorProject(fileProvider);
+
+ var templateEngine = new MvcRazorTemplateEngine(razorEngine, razorProject);
+ var compiler = new RazorCompiler(
+ Mock.Of(),
+ GetCompilerCacheProvider(fileProvider),
+ templateEngine);
+ var codeDocument = templateEngine.CreateCodeDocument(viewPath);
+
+ // Act
+ var csharpDocument = templateEngine.GenerateCode(codeDocument);
+ var compilationResult = compiler.GetCompilationFailedResult(codeDocument, csharpDocument.Diagnostics);
+
+ // Assert
+ var failure = Assert.Single(compilationResult.CompilationFailures);
+ Assert.Equal(physicalPath, failure.SourceFilePath);
+ }
+
+ [Fact]
+ public void GetCompilationFailedResult_ReadsContentFromSourceDocuments()
+ {
+ // Arrange
+ var viewPath = "/Views/Home/Index.cshtml";
+ var fileContent =
+@"
+@if (User.IsAdmin)
+{
+
+}
+";
+
+ var razorEngine = RazorEngine.Create();
+ var fileProvider = new TestFileProvider();
+ fileProvider.AddFile(viewPath, fileContent);
+ var razorProject = new DefaultRazorProject(fileProvider);
+
+ var templateEngine = new MvcRazorTemplateEngine(razorEngine, razorProject);
+ var compiler = new RazorCompiler(
+ Mock.Of(),
+ GetCompilerCacheProvider(fileProvider),
+ templateEngine);
+ var codeDocument = templateEngine.CreateCodeDocument(viewPath);
+
+ // Act
+ var csharpDocument = templateEngine.GenerateCode(codeDocument);
+ var compilationResult = compiler.GetCompilationFailedResult(codeDocument, csharpDocument.Diagnostics);
+
+ // Assert
+ var failure = Assert.Single(compilationResult.CompilationFailures);
+ Assert.Equal(fileContent, failure.SourceFileContent);
+ }
+
+ [Fact]
+ public void GetCompilationFailedResult_ReadsContentFromImports()
+ {
+ // Arrange
+ var viewPath = "/Views/Home/Index.cshtml";
+ var importsFilePath = @"x:\views\_MyImports.cshtml";
+ var fileContent = "@ ";
+ var importsContent = "@(abc";
+
+ var razorEngine = RazorEngine.Create();
+ var fileProvider = new TestFileProvider();
+ fileProvider.AddFile(viewPath, fileContent);
+ var importsFile = fileProvider.AddFile("/Views/_MyImports.cshtml", importsContent);
+ importsFile.PhysicalPath = importsFilePath;
+ var razorProject = new DefaultRazorProject(fileProvider);
+
+ var templateEngine = new MvcRazorTemplateEngine(razorEngine, razorProject)
+ {
+ Options =
+ {
+ ImportsFileName = "_MyImports.cshtml",
+ }
+ };
+ var compiler = new RazorCompiler(
+ Mock.Of(),
+ GetCompilerCacheProvider(fileProvider),
+ templateEngine);
+ var codeDocument = templateEngine.CreateCodeDocument(viewPath);
+
+ // Act
+ var csharpDocument = templateEngine.GenerateCode(codeDocument);
+ var compilationResult = compiler.GetCompilationFailedResult(codeDocument, csharpDocument.Diagnostics);
+
+ // Assert
+ // This expectation is incorrect. https://github.com/aspnet/Razor/issues/1069 needs to be fixed,
+ // which should cause this test to fail.
+ var failure = Assert.Single(compilationResult.CompilationFailures);
+ Assert.Equal(viewPath, failure.SourceFilePath);
+ Assert.Collection(failure.Messages,
+ message =>
+ {
+ Assert.Equal(@"A space or line break was encountered after the ""@"" character. Only valid identifiers, keywords, comments, ""("" and ""{"" are valid at the start of a code block and they must occur immediately following ""@"" with no space in between.",
+ message.Message);
+ },
+ message =>
+ {
+ Assert.Equal(@"The explicit expression block is missing a closing "")"" character. Make sure you have a matching "")"" character for all the ""("" characters within this block, and that none of the "")"" characters are being interpreted as markup.",
+ message.Message);
+
+ });
+ }
+
+ [Fact]
+ public void GetCompilationFailedResult_GroupsMessages()
+ {
+ // Arrange
+ var viewPath = "views/index.razor";
+ var viewImportsPath = "views/global.import.cshtml";
+ var codeDocument = RazorCodeDocument.Create(
+ Create(viewPath, "View Content"),
+ new[] { Create(viewImportsPath, "Global Import Content") });
+ var diagnostics = new[]
+ {
+ GetRazorDiagnostic("message-1", new SourceLocation(1, 2, 17), length: 1),
+ GetRazorDiagnostic("message-2", new SourceLocation(viewPath, 1, 4, 6), length: 7),
+ GetRazorDiagnostic("message-3", SourceLocation.Undefined, length: -1),
+ GetRazorDiagnostic("message-4", new SourceLocation(viewImportsPath, 1, 3, 8), length: 4),
+ };
+ var fileProvider = new TestFileProvider();
+ var compiler = new RazorCompiler(
+ Mock.Of(),
+ GetCompilerCacheProvider(fileProvider),
+ new MvcRazorTemplateEngine(RazorEngine.Create(), new DefaultRazorProject(fileProvider)));
+
+ // Act
+ var result = compiler.GetCompilationFailedResult(codeDocument, diagnostics);
+
+ // Assert
+ Assert.Collection(result.CompilationFailures,
+ failure =>
+ {
+ Assert.Equal(viewPath, failure.SourceFilePath);
+ Assert.Equal("View Content", failure.SourceFileContent);
+ Assert.Collection(failure.Messages,
+ message =>
+ {
+ Assert.Equal(diagnostics[0].GetMessage(), message.Message);
+ Assert.Equal(viewPath, message.SourceFilePath);
+ Assert.Equal(3, message.StartLine);
+ Assert.Equal(17, message.StartColumn);
+ Assert.Equal(3, message.EndLine);
+ Assert.Equal(18, message.EndColumn);
+ },
+ message =>
+ {
+ Assert.Equal(diagnostics[1].GetMessage(), message.Message);
+ Assert.Equal(viewPath, message.SourceFilePath);
+ Assert.Equal(5, message.StartLine);
+ Assert.Equal(6, message.StartColumn);
+ Assert.Equal(5, message.EndLine);
+ Assert.Equal(13, message.EndColumn);
+ },
+ message =>
+ {
+ Assert.Equal(diagnostics[2].GetMessage(), message.Message);
+ Assert.Equal(viewPath, message.SourceFilePath);
+ Assert.Equal(0, message.StartLine);
+ Assert.Equal(-1, message.StartColumn);
+ Assert.Equal(0, message.EndLine);
+ Assert.Equal(-2, message.EndColumn);
+ });
+ },
+ failure =>
+ {
+ Assert.Equal(viewImportsPath, failure.SourceFilePath);
+ Assert.Equal("Global Import Content", failure.SourceFileContent);
+ Assert.Collection(failure.Messages,
+ message =>
+ {
+ Assert.Equal(diagnostics[3].GetMessage(), message.Message);
+ Assert.Equal(viewImportsPath, message.SourceFilePath);
+ Assert.Equal(4, message.StartLine);
+ Assert.Equal(8, message.StartColumn);
+ Assert.Equal(4, message.EndLine);
+ Assert.Equal(12, message.EndColumn);
+ });
+ });
+ }
+
+ private ICompilerCacheProvider GetCompilerCacheProvider(TestFileProvider fileProvider)
+ {
+ var compilerCache = new CompilerCache(fileProvider);
+ var compilerCacheProvider = new Mock();
+ compilerCacheProvider.SetupGet(p => p.Cache).Returns(compilerCache);
+
+ return compilerCacheProvider.Object;
+ }
+
+ private static RazorSourceDocument Create(string path, string template)
+ {
+ var stream = new MemoryStream(Encoding.UTF8.GetBytes(template));
+ return RazorSourceDocument.ReadFrom(stream, path);
+ }
+
+ private static RazorDiagnostic GetRazorDiagnostic(string message, SourceLocation sourceLocation, int length)
+ {
+ var diagnosticDescriptor = new RazorDiagnosticDescriptor("test-id", () => message, RazorDiagnosticSeverity.Error);
+ var sourceSpan = new SourceSpan(sourceLocation, length);
+
+ return RazorDiagnostic.Create(diagnosticDescriptor, sourceSpan);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineTest.cs
index a2fffe78ab..9cc62636f4 100644
--- a/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Razor.Test/RazorViewEngineTest.cs
@@ -7,8 +7,10 @@ using System.Threading;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Abstractions;
+using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.ViewEngines;
+using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Caching.Memory;
@@ -855,7 +857,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
.Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml"))
.Returns(new RazorPageFactoryResult(() => viewStart, new IChangeToken[0]));
- var viewEngine = CreateViewEngine(pageFactory.Object);
+ var fileProvider = new TestFileProvider();
+ var razorProject = new DefaultRazorProject(fileProvider);
+ var viewEngine = CreateViewEngine(pageFactory.Object, razorProject: razorProject);
var context = GetActionContext(_controllerTestContext);
// Act 1
@@ -1302,6 +1306,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
Mock.Of(),
new HtmlTestEncoder(),
GetOptionsAccessor(expanders: null),
+ new DefaultRazorProject(new TestFileProvider()),
loggerFactory);
// Act
@@ -1615,10 +1620,15 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
private TestableRazorViewEngine CreateViewEngine(
IRazorPageFactoryProvider pageFactory = null,
- IEnumerable expanders = null)
+ IEnumerable expanders = null,
+ RazorProject razorProject = null)
{
pageFactory = pageFactory ?? Mock.Of();
- return new TestableRazorViewEngine(pageFactory, GetOptionsAccessor(expanders));
+ if (razorProject == null)
+ {
+ razorProject = new DefaultRazorProject(new TestFileProvider());
+ }
+ return new TestableRazorViewEngine(pageFactory, GetOptionsAccessor(expanders), razorProject);
}
private static IOptions GetOptionsAccessor(
@@ -1707,7 +1717,15 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Test
public TestableRazorViewEngine(
IRazorPageFactoryProvider pageFactory,
IOptions optionsAccessor)
- : base(pageFactory, Mock.Of(), new HtmlTestEncoder(), optionsAccessor, NullLoggerFactory.Instance)
+ : this(pageFactory, optionsAccessor, new DefaultRazorProject(new TestFileProvider()))
+ {
+ }
+
+ public TestableRazorViewEngine(
+ IRazorPageFactoryProvider pageFactory,
+ IOptions optionsAccessor,
+ RazorProject razorProject)
+ : base(pageFactory, Mock.Of(), new HtmlTestEncoder(), optionsAccessor, razorProject, NullLoggerFactory.Instance)
{
}
diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerProviderTest.cs
index 2007250104..f6a61b7bcb 100644
--- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerProviderTest.cs
@@ -210,7 +210,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var fileProvider = new TestFileProvider();
fileProvider.AddFile("/Home/Path1/_PageStart.cshtml", "content1");
fileProvider.AddFile("/_PageStart.cshtml", "content2");
- var defaultRazorProject = new DefaultRazorProject(fileProvider);
+ var defaultRazorProject = new TestRazorProject(fileProvider);
var invokerProvider = CreateInvokerProvider(
loader.Object,
@@ -249,7 +249,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
var razorPageFactoryProvider = new Mock();
var fileProvider = new TestFileProvider();
- var defaultRazorProject = new DefaultRazorProject(fileProvider);
+ var defaultRazorProject = new TestRazorProject(fileProvider);
var invokerProvider = CreateInvokerProvider(
loader.Object,
@@ -592,7 +592,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
fileProvider.AddFile("/Views/_PageStart.cshtml", "@page starts!");
fileProvider.AddFile("/Views/Deeper/_PageStart.cshtml", "page content");
- var razorProject = CreateRazorProject(fileProvider);
+ var razorProject = new TestRazorProject(fileProvider);
var mock = new Mock();
mock.Setup(p => p.CreateFactory("/Views/Deeper/_PageStart.cshtml"))
@@ -642,8 +642,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
// No files
var fileProvider = new TestFileProvider();
-
- var razorProject = CreateRazorProject(fileProvider);
+ var razorProject = new TestRazorProject(fileProvider);
var invokerProvider = CreateInvokerProvider(
loader.Object,
@@ -670,11 +669,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
return mock.Object;
}
- private RazorProject CreateRazorProject(IFileProvider fileProvider)
- {
- return new DefaultRazorProject(fileProvider);
- }
-
private static CompiledPageActionDescriptor CreateCompiledPageActionDescriptor(
PageActionDescriptor descriptor,
Type pageType = null)
diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/TestRazorProject.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/TestRazorProject.cs
new file mode 100644
index 0000000000..c7ab938f33
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/TestRazorProject.cs
@@ -0,0 +1,26 @@
+// 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 Microsoft.AspNetCore.Mvc.Razor.Internal;
+using Microsoft.Extensions.FileProviders;
+using Moq;
+
+namespace Microsoft.AspNetCore.Mvc.RazorPages
+{
+ public class TestRazorProject : DefaultRazorProject
+ {
+ public TestRazorProject(IFileProvider fileProvider)
+ :base(GetAccessor(fileProvider))
+ {
+ }
+
+ private static IRazorViewEngineFileProviderAccessor GetAccessor(IFileProvider fileProvider)
+ {
+ var fileProviderAccessor = new Mock();
+ fileProviderAccessor.SetupGet(f => f.FileProvider)
+ .Returns(fileProvider);
+
+ return fileProviderAccessor.Object;
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Mvc.TestCommon/TestFileProvider.cs b/test/Microsoft.AspNetCore.Mvc.TestCommon/TestFileProvider.cs
index c432ff5ad3..d6efad2070 100644
--- a/test/Microsoft.AspNetCore.Mvc.TestCommon/TestFileProvider.cs
+++ b/test/Microsoft.AspNetCore.Mvc.TestCommon/TestFileProvider.cs
@@ -74,6 +74,14 @@ namespace Microsoft.AspNetCore.Mvc.Razor
}
}
+ public virtual TestFileChangeToken AddChangeToken(string filter)
+ {
+ var changeToken = new TestFileChangeToken();
+ _fileTriggers[filter] = changeToken;
+
+ return changeToken;
+ }
+
public virtual IChangeToken Watch(string filter)
{
TestFileChangeToken changeToken;
diff --git a/test/WebSites/RazorPageExecutionInstrumentationWebSite/Startup.cs b/test/WebSites/RazorPageExecutionInstrumentationWebSite/Startup.cs
index 3341cc32a8..7d9689769a 100644
--- a/test/WebSites/RazorPageExecutionInstrumentationWebSite/Startup.cs
+++ b/test/WebSites/RazorPageExecutionInstrumentationWebSite/Startup.cs
@@ -5,7 +5,7 @@ using System.Diagnostics;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
-using Microsoft.AspNetCore.Mvc.Razor.Compilation;
+using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.Extensions.DependencyInjection;
namespace RazorPageExecutionInstrumentationWebSite
@@ -16,7 +16,7 @@ namespace RazorPageExecutionInstrumentationWebSite
public void ConfigureServices(IServiceCollection services)
{
// Normalize line endings to avoid changes in instrumentation locations between systems.
- services.AddTransient();
+ services.AddTransient();
// Add MVC services to the services container.
services.AddMvc();
diff --git a/test/WebSites/RazorPageExecutionInstrumentationWebSite/TestRazorCompilationService.cs b/test/WebSites/RazorPageExecutionInstrumentationWebSite/TestRazorCompilationService.cs
index 17482281e1..98c4842fe9 100644
--- a/test/WebSites/RazorPageExecutionInstrumentationWebSite/TestRazorCompilationService.cs
+++ b/test/WebSites/RazorPageExecutionInstrumentationWebSite/TestRazorCompilationService.cs
@@ -3,40 +3,45 @@
using System.IO;
using System.Text;
-using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Razor.Evolution;
-using Microsoft.Extensions.Logging;
namespace RazorPageExecutionInstrumentationWebSite
{
- public class TestRazorCompilationService : RazorCompilationService
+ public class TestRazorProject : DefaultRazorProject
{
- public TestRazorCompilationService(
- ICompilationService compilationService,
- RazorEngine engine,
- RazorProject project,
- IRazorViewEngineFileProviderAccessor fileProviderAccessor,
- ILoggerFactory loggerFactory)
- : base(compilationService, engine, project, fileProviderAccessor, loggerFactory)
+ public TestRazorProject(IRazorViewEngineFileProviderAccessor fileProviderAccessor)
+ : base(fileProviderAccessor)
{
}
- public override RazorCodeDocument CreateCodeDocument(string relativePath, Stream inputStream)
+ public override RazorProjectItem GetItem(string path)
{
- // Normalize line endings to '\r\n' (CRLF). This removes core.autocrlf, core.eol, core.safecrlf, and
- // .gitattributes from the equation and treats "\r\n" and "\n" as equivalent. Does not handle
- // some line endings like "\r" but otherwise ensures checksums and line mappings are consistent.
- string text;
- using (var streamReader = new StreamReader(inputStream))
+ var item = (DefaultRazorProjectItem)base.GetItem(path);
+ return new TestRazorProjectItem(item);
+ }
+
+ private class TestRazorProjectItem : DefaultRazorProjectItem
+ {
+ public TestRazorProjectItem(DefaultRazorProjectItem projectItem)
+ : base(projectItem.FileInfo, projectItem.BasePath, projectItem.Path)
{
- text = streamReader.ReadToEnd().Replace("\r", "").Replace("\n", "\r\n");
}
- var bytes = Encoding.UTF8.GetBytes(text);
- inputStream = new MemoryStream(bytes);
+ public override Stream Read()
+ {
+ // Normalize line endings to '\r\n' (CRLF). This removes core.autocrlf, core.eol, core.safecrlf, and
+ // .gitattributes from the equation and treats "\r\n" and "\n" as equivalent. Does not handle
+ // some line endings like "\r" but otherwise ensures checksums and line mappings are consistent.
+ string text;
+ using (var streamReader = new StreamReader(base.Read()))
+ {
+ text = streamReader.ReadToEnd().Replace("\r", "").Replace("\n", "\r\n");
+ }
- return base.CreateCodeDocument(relativePath, inputStream);
+ var bytes = Encoding.UTF8.GetBytes(text);
+ return new MemoryStream(bytes);
+ }
}
}
}
\ No newline at end of file