parent
845b86963e
commit
380a93d370
|
|
@ -10,83 +10,63 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the result of compilation.
|
/// Represents the result of compilation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CompilationResult
|
public struct CompilationResult
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of <see cref="CompilationResult"/>.
|
/// Initializes a new instance of <see cref="CompilationResult"/> for a successful compilation.
|
||||||
/// </summary>
|
|
||||||
protected CompilationResult()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets (or sets in derived types) the type produced as a result of compilation.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>This property is <c>null</c> when compilation failed.</remarks>
|
|
||||||
public Type CompiledType { get; protected set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets (or sets in derived types) the generated C# content that was compiled.
|
|
||||||
/// </summary>
|
|
||||||
public string CompiledContent { get; protected set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the <see cref="CompilationFailure"/>s produced from parsing or compiling the Razor file.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>This property is <c>null</c> when compilation succeeded. An empty sequence
|
|
||||||
/// indicates a failed compilation.</remarks>
|
|
||||||
public IEnumerable<CompilationFailure> CompilationFailures { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the <see cref="CompilationResult"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The current <see cref="CompilationResult"/> instance.</returns>
|
|
||||||
/// <exception cref="CompilationFailedException">Thrown if compilation failed.</exception>
|
|
||||||
public CompilationResult EnsureSuccessful()
|
|
||||||
{
|
|
||||||
if (CompilationFailures != null)
|
|
||||||
{
|
|
||||||
throw new CompilationFailedException(CompilationFailures);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a <see cref="CompilationResult"/> for a failed compilation.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="compilationFailures"><see cref="CompilationFailure"/>s produced from parsing or
|
|
||||||
/// compiling the Razor file.</param>
|
|
||||||
/// <returns>A <see cref="CompilationResult"/> instance for a failed compilation.</returns>
|
|
||||||
public static CompilationResult Failed(IEnumerable<CompilationFailure> compilationFailures)
|
|
||||||
{
|
|
||||||
if (compilationFailures == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(compilationFailures));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new CompilationResult
|
|
||||||
{
|
|
||||||
CompilationFailures = compilationFailures
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a <see cref="CompilationResult"/> for a successful compilation.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">The compiled type.</param>
|
/// <param name="type">The compiled type.</param>
|
||||||
/// <returns>A <see cref="CompilationResult"/> instance for a successful compilation.</returns>
|
public CompilationResult(Type type)
|
||||||
public static CompilationResult Successful(Type type)
|
|
||||||
{
|
{
|
||||||
if (type == null)
|
if (type == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(type));
|
throw new ArgumentNullException(nameof(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CompilationResult
|
CompiledType = type;
|
||||||
|
CompilationFailures = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="CompilationResult"/> for a failed compilation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="compilationFailures"><see cref="CompilationFailure"/>s produced from parsing or
|
||||||
|
/// compiling the Razor file.</param>
|
||||||
|
public CompilationResult(IEnumerable<CompilationFailure> compilationFailures)
|
||||||
|
{
|
||||||
|
if (compilationFailures == null)
|
||||||
{
|
{
|
||||||
CompiledType = type
|
throw new ArgumentNullException(nameof(compilationFailures));
|
||||||
};
|
}
|
||||||
|
|
||||||
|
CompiledType = null;
|
||||||
|
CompilationFailures = compilationFailures;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type produced as a result of compilation.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This property is <c>null</c> when compilation failed.</remarks>
|
||||||
|
public Type CompiledType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="CompilationFailure"/>s produced from parsing or compiling the Razor file.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This property is <c>null</c> when compilation succeeded. An empty sequence
|
||||||
|
/// indicates a failed compilation.</remarks>
|
||||||
|
public IEnumerable<CompilationFailure> CompilationFailures { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="CompilationResult"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The current <see cref="CompilationResult"/> instance.</returns>
|
||||||
|
/// <exception cref="CompilationFailedException">Thrown if compilation failed.</exception>
|
||||||
|
public void EnsureSuccessful()
|
||||||
|
{
|
||||||
|
if (CompilationFailures != null)
|
||||||
|
{
|
||||||
|
throw new CompilationFailedException(CompilationFailures);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using System.Diagnostics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNet.FileProviders;
|
using Microsoft.AspNet.FileProviders;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
{
|
{
|
||||||
|
|
@ -56,7 +57,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
|
|
||||||
foreach (var item in precompiledViews)
|
foreach (var item in precompiledViews)
|
||||||
{
|
{
|
||||||
var cacheEntry = new CompilerCacheResult(CompilationResult.Successful(item.Value));
|
var cacheEntry = new CompilerCacheResult(new CompilationResult(item.Value));
|
||||||
_cache.Set(GetNormalizedPath(item.Key), cacheEntry);
|
_cache.Set(GetNormalizedPath(item.Key), cacheEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -95,33 +96,29 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
string normalizedPath,
|
string normalizedPath,
|
||||||
Func<RelativeFileInfo, CompilationResult> compile)
|
Func<RelativeFileInfo, CompilationResult> compile)
|
||||||
{
|
{
|
||||||
CompilerCacheResult cacheResult;
|
|
||||||
var fileInfo = _fileProvider.GetFileInfo(normalizedPath);
|
var fileInfo = _fileProvider.GetFileInfo(normalizedPath);
|
||||||
MemoryCacheEntryOptions cacheEntryOptions;
|
MemoryCacheEntryOptions cacheEntryOptions;
|
||||||
CompilerCacheResult cacheResultToCache;
|
CompilerCacheResult cacheResult;
|
||||||
if (!fileInfo.Exists)
|
if (!fileInfo.Exists)
|
||||||
{
|
{
|
||||||
cacheResultToCache = CompilerCacheResult.FileNotFound;
|
var expirationToken = _fileProvider.Watch(normalizedPath);
|
||||||
cacheResult = CompilerCacheResult.FileNotFound;
|
cacheResult = new CompilerCacheResult(new[] { expirationToken });
|
||||||
|
|
||||||
cacheEntryOptions = new MemoryCacheEntryOptions();
|
cacheEntryOptions = new MemoryCacheEntryOptions();
|
||||||
cacheEntryOptions.AddExpirationToken(_fileProvider.Watch(normalizedPath));
|
cacheEntryOptions.AddExpirationToken(expirationToken);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var relativeFileInfo = new RelativeFileInfo(fileInfo, normalizedPath);
|
var relativeFileInfo = new RelativeFileInfo(fileInfo, normalizedPath);
|
||||||
var compilationResult = compile(relativeFileInfo).EnsureSuccessful();
|
var compilationResult = compile(relativeFileInfo);
|
||||||
|
compilationResult.EnsureSuccessful();
|
||||||
cacheEntryOptions = GetMemoryCacheEntryOptions(normalizedPath);
|
cacheEntryOptions = GetMemoryCacheEntryOptions(normalizedPath);
|
||||||
|
cacheResult = new CompilerCacheResult(
|
||||||
// By default the CompilationResult returned by IRoslynCompiler is an instance of
|
compilationResult,
|
||||||
// UncachedCompilationResult. This type has the generated code as a string property and do not want
|
cacheEntryOptions.ExpirationTokens);
|
||||||
// to cache it. We'll instead cache the unwrapped result.
|
|
||||||
cacheResultToCache = new CompilerCacheResult(
|
|
||||||
CompilationResult.Successful(compilationResult.CompiledType));
|
|
||||||
cacheResult = new CompilerCacheResult(compilationResult);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_cache.Set(normalizedPath, cacheResultToCache, cacheEntryOptions);
|
_cache.Set(normalizedPath, cacheResult, cacheEntryOptions);
|
||||||
return cacheResult;
|
return cacheResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,46 +2,77 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Result of <see cref="ICompilerCache"/>.
|
/// Result of <see cref="ICompilerCache"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CompilerCacheResult
|
public struct CompilerCacheResult
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Result of <see cref="ICompilerCache"/> when the specified file does not exist in the
|
|
||||||
/// file system.
|
|
||||||
/// </summary>
|
|
||||||
public static CompilerCacheResult FileNotFound { get; } = new CompilerCacheResult();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of <see cref="CompilerCacheResult"/> with the specified
|
/// Initializes a new instance of <see cref="CompilerCacheResult"/> with the specified
|
||||||
/// <see cref="CompilationResult"/>.
|
/// <see cref="Compilation.CompilationResult"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="compilationResult">The <see cref="Compilation.CompilationResult"/> </param>
|
/// <param name="compilationResult">The <see cref="Compilation.CompilationResult"/>.</param>
|
||||||
public CompilerCacheResult(CompilationResult compilationResult)
|
public CompilerCacheResult(CompilationResult compilationResult)
|
||||||
|
: this(compilationResult, new IChangeToken[0])
|
||||||
{
|
{
|
||||||
if (compilationResult == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(compilationResult));
|
|
||||||
}
|
|
||||||
|
|
||||||
CompilationResult = compilationResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of <see cref="CompilerCacheResult"/> for a failed file lookup.
|
/// Initializes a new instance of <see cref="CompilerCacheResult"/> with the specified
|
||||||
|
/// <see cref="Compilation.CompilationResult"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected CompilerCacheResult()
|
/// <param name="compilationResult">The <see cref="Compilation.CompilationResult"/>.</param>
|
||||||
|
/// <param name="expirationTokens">One or more <see cref="IChangeToken"/> instances that indicate when
|
||||||
|
/// this result has expired.</param>
|
||||||
|
public CompilerCacheResult(CompilationResult compilationResult, IList<IChangeToken> expirationTokens)
|
||||||
{
|
{
|
||||||
|
if (expirationTokens == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(expirationTokens));
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationResult = compilationResult;
|
||||||
|
Success = true;
|
||||||
|
ExpirationTokens = expirationTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="CompilerCacheResult"/> for a file that could not be
|
||||||
|
/// found in the file system.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="expirationTokens">One or more <see cref="IChangeToken"/> instances that indicate when
|
||||||
|
/// this result has expired.</param>
|
||||||
|
public CompilerCacheResult(IList<IChangeToken> expirationTokens)
|
||||||
|
{
|
||||||
|
if (expirationTokens == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(expirationTokens));
|
||||||
|
}
|
||||||
|
|
||||||
|
CompilationResult = default(CompilationResult);
|
||||||
|
Success = false;
|
||||||
|
ExpirationTokens = expirationTokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="Compilation.CompilationResult"/>.
|
/// The <see cref="Compilation.CompilationResult"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>This property is null when file lookup failed.</remarks>
|
/// <remarks>This property is not available when <see cref="Success"/> is <c>false</c>.</remarks>
|
||||||
public CompilationResult CompilationResult { get; }
|
public CompilationResult CompilationResult { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="IChangeToken"/> instances that indicate when this result has expired.
|
||||||
|
/// </summary>
|
||||||
|
public IList<IChangeToken> ExpirationTokens { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value that determines if the view was successfully found and compiled.
|
||||||
|
/// </summary>
|
||||||
|
public bool Success { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +100,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
failures.Add(compilationFailure);
|
failures.Add(compilationFailure);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompilationResult.Failed(failures);
|
return new CompilationResult(failures);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DiagnosticMessage CreateDiagnosticMessage(RazorError error, string filePath)
|
private DiagnosticMessage CreateDiagnosticMessage(RazorError error, string filePath)
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
var type = assembly.GetExportedTypes()
|
var type = assembly.GetExportedTypes()
|
||||||
.First(t => t.Name.StartsWith(_classPrefix, StringComparison.Ordinal));
|
.First(t => t.Name.StartsWith(_classPrefix, StringComparison.Ordinal));
|
||||||
|
|
||||||
return UncachedCompilationResult.Successful(type, compilationContent);
|
return new CompilationResult(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -214,7 +214,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
failures.Add(compilationFailure);
|
failures.Add(compilationFailure);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompilationResult.Failed(failures);
|
return new CompilationResult(failures);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetFilePath(string relativePath, Diagnostic diagnostic)
|
private static string GetFilePath(string relativePath, Diagnostic diagnostic)
|
||||||
|
|
|
||||||
|
|
@ -1,47 +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;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the result of compilation that does not come from the <see cref="ICompilerCache" />.
|
|
||||||
/// </summary>
|
|
||||||
public class UncachedCompilationResult : CompilationResult
|
|
||||||
{
|
|
||||||
private UncachedCompilationResult()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public string RazorFileContent { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a <see cref="UncachedCompilationResult"/> that represents a success in compilation.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">The compiled type.</param>
|
|
||||||
/// <param name="compiledContent">The generated C# content that was compiled.</param>
|
|
||||||
/// <returns>An <see cref="UncachedCompilationResult"/> instance that indicates a successful
|
|
||||||
/// compilation.</returns>
|
|
||||||
public static UncachedCompilationResult Successful(
|
|
||||||
Type type,
|
|
||||||
string compiledContent)
|
|
||||||
{
|
|
||||||
if (type == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (compiledContent == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(compiledContent));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new UncachedCompilationResult
|
|
||||||
{
|
|
||||||
CompiledType = type,
|
|
||||||
CompiledContent = compiledContent,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -7,10 +7,10 @@ using Microsoft.AspNet.Mvc.Razor.Compilation;
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a <see cref="IRazorPageFactory"/> that creates <see cref="RazorPage"/> instances
|
/// Represents a <see cref="IRazorPageFactoryProvider"/> that creates <see cref="RazorPage"/> instances
|
||||||
/// from razor files in the file system.
|
/// from razor files in the file system.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class VirtualPathRazorPageFactory : IRazorPageFactory
|
public class DefaultRazorPageFactoryProvider : IRazorPageFactoryProvider
|
||||||
{
|
{
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This delegate holds on to an instance of <see cref="IRazorCompilationService"/>.
|
/// This delegate holds on to an instance of <see cref="IRazorCompilationService"/>.
|
||||||
|
|
@ -20,11 +20,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
private ICompilerCache _compilerCache;
|
private ICompilerCache _compilerCache;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of <see cref="VirtualPathRazorPageFactory"/>.
|
/// Initializes a new instance of <see cref="DefaultRazorPageFactoryProvider"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="razorCompilationService">The <see cref="IRazorCompilationService"/>.</param>
|
/// <param name="razorCompilationService">The <see cref="IRazorCompilationService"/>.</param>
|
||||||
/// <param name="compilerCacheProvider">The <see cref="ICompilerCacheProvider"/>.</param>
|
/// <param name="compilerCacheProvider">The <see cref="ICompilerCacheProvider"/>.</param>
|
||||||
public VirtualPathRazorPageFactory(
|
public DefaultRazorPageFactoryProvider(
|
||||||
IRazorCompilationService razorCompilationService,
|
IRazorCompilationService razorCompilationService,
|
||||||
ICompilerCacheProvider compilerCacheProvider)
|
ICompilerCacheProvider compilerCacheProvider)
|
||||||
{
|
{
|
||||||
|
|
@ -46,7 +46,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IRazorPage CreateInstance(string relativePath)
|
public RazorPageFactoryResult CreateFactory(string relativePath)
|
||||||
{
|
{
|
||||||
if (relativePath == null)
|
if (relativePath == null)
|
||||||
{
|
{
|
||||||
|
|
@ -58,18 +58,33 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
// For tilde slash paths, drop the leading ~ to make it work with the underlying IFileProvider.
|
// For tilde slash paths, drop the leading ~ to make it work with the underlying IFileProvider.
|
||||||
relativePath = relativePath.Substring(1);
|
relativePath = relativePath.Substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = CompilerCache.GetOrAdd(relativePath, _compileDelegate);
|
var result = CompilerCache.GetOrAdd(relativePath, _compileDelegate);
|
||||||
|
if (result.Success)
|
||||||
if (result == CompilerCacheResult.FileNotFound)
|
|
||||||
{
|
{
|
||||||
return null;
|
var pageFactory = GetPageFactory(result.CompilationResult.CompiledType, relativePath);
|
||||||
|
return new RazorPageFactoryResult(pageFactory, result.ExpirationTokens);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new RazorPageFactoryResult(result.ExpirationTokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var page = (IRazorPage)Activator.CreateInstance(result.CompilationResult.CompiledType);
|
/// <summary>
|
||||||
page.Path = relativePath;
|
/// Creates a factory for <see cref="IRazorPage"/>.
|
||||||
|
/// </summary>
|
||||||
return page;
|
/// <param name="compiledType">The <see cref="Type"/> to produce an instance of <see cref="IRazorPage"/>
|
||||||
|
/// from.</param>
|
||||||
|
/// <param name="relativePath">The application relative path of the page.</param>
|
||||||
|
/// <returns>A factory for <paramref name="compiledType"/>.</returns>
|
||||||
|
protected virtual Func<IRazorPage> GetPageFactory(Type compiledType, string relativePath)
|
||||||
|
{
|
||||||
|
return () =>
|
||||||
|
{
|
||||||
|
var page = (IRazorPage)Activator.CreateInstance(compiledType);
|
||||||
|
page.Path = relativePath;
|
||||||
|
return page;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,176 +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.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Default implementation of <see cref="IViewLocationCache"/>.
|
|
||||||
/// </summary>
|
|
||||||
public class DefaultViewLocationCache : IViewLocationCache
|
|
||||||
{
|
|
||||||
// A mapping of keys generated from ViewLocationExpanderContext to view locations.
|
|
||||||
private readonly ConcurrentDictionary<ViewLocationCacheKey, ViewLocationCacheResult> _cache;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of <see cref="DefaultViewLocationCache"/>.
|
|
||||||
/// </summary>
|
|
||||||
public DefaultViewLocationCache()
|
|
||||||
{
|
|
||||||
_cache = new ConcurrentDictionary<ViewLocationCacheKey, ViewLocationCacheResult>(
|
|
||||||
ViewLocationCacheKeyComparer.Instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public ViewLocationCacheResult Get(ViewLocationExpanderContext context)
|
|
||||||
{
|
|
||||||
if (context == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
var cacheKey = GenerateKey(context, copyViewExpanderValues: false);
|
|
||||||
ViewLocationCacheResult result;
|
|
||||||
if (_cache.TryGetValue(cacheKey, out result))
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ViewLocationCacheResult.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Set(
|
|
||||||
ViewLocationExpanderContext context,
|
|
||||||
ViewLocationCacheResult value)
|
|
||||||
{
|
|
||||||
if (context == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
var cacheKey = GenerateKey(context, copyViewExpanderValues: true);
|
|
||||||
_cache.TryAdd(cacheKey, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal for unit testing
|
|
||||||
internal static ViewLocationCacheKey GenerateKey(
|
|
||||||
ViewLocationExpanderContext context,
|
|
||||||
bool copyViewExpanderValues)
|
|
||||||
{
|
|
||||||
var controller = RazorViewEngine.GetNormalizedRouteValue(
|
|
||||||
context.ActionContext,
|
|
||||||
RazorViewEngine.ControllerKey);
|
|
||||||
|
|
||||||
var area = RazorViewEngine.GetNormalizedRouteValue(
|
|
||||||
context.ActionContext,
|
|
||||||
RazorViewEngine.AreaKey);
|
|
||||||
|
|
||||||
|
|
||||||
var values = context.Values;
|
|
||||||
if (values != null && copyViewExpanderValues)
|
|
||||||
{
|
|
||||||
// When performing a Get, avoid creating a copy of the values dictionary
|
|
||||||
values = new Dictionary<string, string>(values, StringComparer.Ordinal);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ViewLocationCacheKey(
|
|
||||||
context.ViewName,
|
|
||||||
controller,
|
|
||||||
area,
|
|
||||||
context.IsPartial,
|
|
||||||
values);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal for unit testing
|
|
||||||
internal class ViewLocationCacheKeyComparer : IEqualityComparer<ViewLocationCacheKey>
|
|
||||||
{
|
|
||||||
public static readonly ViewLocationCacheKeyComparer Instance = new ViewLocationCacheKeyComparer();
|
|
||||||
|
|
||||||
public bool Equals(ViewLocationCacheKey x, ViewLocationCacheKey y)
|
|
||||||
{
|
|
||||||
if (x.IsPartial != y.IsPartial ||
|
|
||||||
!string.Equals(x.ViewName, y.ViewName, StringComparison.Ordinal) ||
|
|
||||||
!string.Equals(x.ControllerName, y.ControllerName, StringComparison.Ordinal) ||
|
|
||||||
!string.Equals(x.AreaName, y.AreaName, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ReferenceEquals(x.Values, y.Values))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x.Values == null || y.Values == null || (x.Values.Count != y.Values.Count))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var item in x.Values)
|
|
||||||
{
|
|
||||||
string yValue;
|
|
||||||
if (!y.Values.TryGetValue(item.Key, out yValue) ||
|
|
||||||
!string.Equals(item.Value, yValue, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetHashCode(ViewLocationCacheKey key)
|
|
||||||
{
|
|
||||||
var hashCodeCombiner = HashCodeCombiner.Start();
|
|
||||||
hashCodeCombiner.Add(key.IsPartial ? 1 : 0);
|
|
||||||
hashCodeCombiner.Add(key.ViewName, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(key.ControllerName, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(key.AreaName, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
if (key.Values != null)
|
|
||||||
{
|
|
||||||
foreach (var item in key.Values)
|
|
||||||
{
|
|
||||||
hashCodeCombiner.Add(item.Key, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(item.Value, StringComparer.Ordinal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashCodeCombiner;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal for unit testing
|
|
||||||
internal struct ViewLocationCacheKey
|
|
||||||
{
|
|
||||||
public ViewLocationCacheKey(
|
|
||||||
string viewName,
|
|
||||||
string controllerName,
|
|
||||||
string areaName,
|
|
||||||
bool isPartial,
|
|
||||||
IDictionary<string, string> values)
|
|
||||||
{
|
|
||||||
ViewName = viewName;
|
|
||||||
ControllerName = controllerName;
|
|
||||||
AreaName = areaName;
|
|
||||||
IsPartial = isPartial;
|
|
||||||
Values = values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ViewName { get; }
|
|
||||||
|
|
||||||
public string ControllerName { get; }
|
|
||||||
|
|
||||||
public string AreaName { get; }
|
|
||||||
|
|
||||||
public bool IsPartial { get; }
|
|
||||||
|
|
||||||
public IDictionary<string, string> Values { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -131,8 +131,6 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
|
||||||
services.TryAddSingleton<IRazorViewEngine, RazorViewEngine>();
|
services.TryAddSingleton<IRazorViewEngine, RazorViewEngine>();
|
||||||
|
|
||||||
// Caches view locations that are valid for the lifetime of the application.
|
|
||||||
services.TryAddSingleton<IViewLocationCache, DefaultViewLocationCache>();
|
|
||||||
services.TryAdd(ServiceDescriptor.Singleton<IChunkTreeCache>(serviceProvider =>
|
services.TryAdd(ServiceDescriptor.Singleton<IChunkTreeCache>(serviceProvider =>
|
||||||
{
|
{
|
||||||
var cachedFileProvider = serviceProvider.GetRequiredService<IOptions<RazorViewEngineOptions>>();
|
var cachedFileProvider = serviceProvider.GetRequiredService<IOptions<RazorViewEngineOptions>>();
|
||||||
|
|
@ -147,10 +145,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
|
||||||
// In the default scenario the following services are singleton by virtue of being initialized as part of
|
// In the default scenario the following services are singleton by virtue of being initialized as part of
|
||||||
// creating the singleton RazorViewEngine instance.
|
// creating the singleton RazorViewEngine instance.
|
||||||
services.TryAddTransient<IRazorViewFactory, RazorViewFactory>();
|
services.TryAddTransient<IRazorPageFactoryProvider, DefaultRazorPageFactoryProvider>();
|
||||||
services.TryAddTransient<IRazorPageFactory, VirtualPathRazorPageFactory>();
|
|
||||||
services.TryAddTransient<IRazorCompilationService, RazorCompilationService>();
|
services.TryAddTransient<IRazorCompilationService, RazorCompilationService>();
|
||||||
services.TryAddTransient<IViewStartProvider, ViewStartProvider>();
|
|
||||||
services.TryAddTransient<IMvcRazorHost, MvcRazorHost>();
|
services.TryAddTransient<IMvcRazorHost, MvcRazorHost>();
|
||||||
|
|
||||||
// This caches Razor page activation details that are valid for the lifetime of the application.
|
// This caches Razor page activation details that are valid for the lifetime of the application.
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines methods that are used for creating <see cref="IRazorPage"/> instances at a given path.
|
/// Defines methods that are used for creating <see cref="IRazorPage"/> instances at a given path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IRazorPageFactory
|
public interface IRazorPageFactoryProvider
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="IRazorPage"/> for the specified path.
|
/// Creates a <see cref="IRazorPage"/> factory for the specified path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="relativePath">The path to locate the page.</param>
|
/// <param name="relativePath">The path to locate the page.</param>
|
||||||
/// <returns>The IRazorPage instance if it exists, null otherwise.</returns>
|
/// <returns>The <see cref="RazorPageFactoryResult"/> instance.</returns>
|
||||||
IRazorPage CreateInstance(string relativePath);
|
RazorPageFactoryResult CreateFactory(string relativePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,23 +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 Microsoft.AspNet.Mvc.ViewEngines;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Defines methods to create <see cref="RazorView"/> instances with a given <see cref="IRazorPage"/>.
|
|
||||||
/// </summary>
|
|
||||||
public interface IRazorViewFactory
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a <see cref="RazorView"/> providing it with the <see cref="IRazorPage"/> to execute.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="viewEngine">The <see cref="IRazorViewEngine"/> that was used to locate Layout pages
|
|
||||||
/// that will be part of <paramref name="page"/>'s execution.</param>
|
|
||||||
/// <param name="page">The <see cref="IRazorPage"/> instance to execute.</param>
|
|
||||||
/// <param name="isPartial">Determines if the view is to be executed as a partial.</param>
|
|
||||||
/// <returns>A <see cref="IView"/> instance that renders the contents of the <paramref name="page"/></returns>
|
|
||||||
IView GetView(IRazorViewEngine viewEngine, IRazorPage page, bool isPartial);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +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.AspNet.Mvc.Razor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Specifies the contracts for caching view locations generated by <see cref="IViewLocationExpander"/>.
|
|
||||||
/// </summary>
|
|
||||||
public interface IViewLocationCache
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a cached view location based on the specified <paramref name="context"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context">The <see cref="ViewLocationExpanderContext"/> for the current view location
|
|
||||||
/// expansion.</param>
|
|
||||||
/// <returns>The cached location, if available, <c>null</c> otherwise.</returns>
|
|
||||||
ViewLocationCacheResult Get(ViewLocationExpanderContext context);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a cache entry for values specified by <paramref name="context"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context">The <see cref="ViewLocationExpanderContext"/> for the current view location
|
|
||||||
/// expansion.</param>
|
|
||||||
/// <param name="value">The view location that is to be cached.</param>
|
|
||||||
void Set(ViewLocationExpanderContext context, ViewLocationCacheResult value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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.
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Defines methods for locating ViewStart pages that are applicable to a page.
|
|
||||||
/// </summary>
|
|
||||||
public interface IViewStartProvider
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Given a view path, returns a sequence of ViewStart instances
|
|
||||||
/// that are applicable to the specified view.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path of the page to locate ViewStart files for.</param>
|
|
||||||
/// <returns>A sequence of <see cref="IRazorPage"/> that represent ViewStart.</returns>
|
|
||||||
IEnumerable<IRazorPage> GetViewStartPages(string path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -146,8 +146,9 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GeneratePrecompiledAssembly(syntaxTrees.Where(tree => tree != null),
|
return GeneratePrecompiledAssembly(
|
||||||
razorFiles.Where(file => file != null));
|
syntaxTrees.Where(tree => tree != null),
|
||||||
|
razorFiles.Where(file => file != null));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual RazorFileInfoCollection GeneratePrecompiledAssembly(
|
protected virtual RazorFileInfoCollection GeneratePrecompiledAssembly(
|
||||||
|
|
@ -174,13 +175,15 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation
|
||||||
var references = CompileContext.Compilation.References
|
var references = CompileContext.Compilation.References
|
||||||
.Concat(new[] { applicationReference });
|
.Concat(new[] { applicationReference });
|
||||||
|
|
||||||
var preCompilationOptions = CompilationSettings.CompilationOptions
|
var preCompilationOptions = CompilationSettings
|
||||||
.WithOutputKind(OutputKind.DynamicallyLinkedLibrary);
|
.CompilationOptions
|
||||||
|
.WithOutputKind(OutputKind.DynamicallyLinkedLibrary);
|
||||||
|
|
||||||
var compilation = CSharpCompilation.Create(assemblyResourceName,
|
var compilation = CSharpCompilation.Create(
|
||||||
options: preCompilationOptions,
|
assemblyResourceName,
|
||||||
syntaxTrees: syntaxTrees,
|
options: preCompilationOptions,
|
||||||
references: references);
|
syntaxTrees: syntaxTrees,
|
||||||
|
references: references);
|
||||||
|
|
||||||
var generateSymbols = GenerateSymbols && SymbolsUtility.SupportsSymbolsGeneration();
|
var generateSymbols = GenerateSymbols && SymbolsUtility.SupportsSymbolsGeneration();
|
||||||
// These streams are returned to the runtime and consequently cannot be disposed.
|
// These streams are returned to the runtime and consequently cannot be disposed.
|
||||||
|
|
@ -279,9 +282,10 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation
|
||||||
|
|
||||||
if (results.Success)
|
if (results.Success)
|
||||||
{
|
{
|
||||||
var syntaxTree = SyntaxTreeGenerator.Generate(results.GeneratedCode,
|
var syntaxTree = SyntaxTreeGenerator.Generate(
|
||||||
fileInfo.FileInfo.PhysicalPath,
|
results.GeneratedCode,
|
||||||
CompilationSettings);
|
fileInfo.FileInfo.PhysicalPath,
|
||||||
|
CompilationSettings);
|
||||||
var fullTypeName = results.GetMainClassName(host, syntaxTree);
|
var fullTypeName = results.GetMainClassName(host, syntaxTree);
|
||||||
|
|
||||||
if (fullTypeName != null)
|
if (fullTypeName != null)
|
||||||
|
|
@ -297,9 +301,10 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var diagnostics = results.ParserErrors
|
var diagnostics = results
|
||||||
.Select(error => error.ToDiagnostics(fileInfo.FileInfo.PhysicalPath))
|
.ParserErrors
|
||||||
.ToList();
|
.Select(error => error.ToDiagnostics(fileInfo.FileInfo.PhysicalPath))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
return new PrecompilationCacheEntry(diagnostics);
|
return new PrecompilationCacheEntry(diagnostics);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
// 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.Extensions.Primitives;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Result of <see cref="IRazorPageFactoryProvider.CreateFactory(string)"/>.
|
||||||
|
/// </summary>
|
||||||
|
public struct RazorPageFactoryResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="RazorPageFactoryResult"/> with the
|
||||||
|
/// specified <paramref name="expirationTokens"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="expirationTokens">One or more <see cref="IChangeToken"/> instances.</param>
|
||||||
|
public RazorPageFactoryResult(IList<IChangeToken> expirationTokens)
|
||||||
|
{
|
||||||
|
if (expirationTokens == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(expirationTokens));
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpirationTokens = expirationTokens;
|
||||||
|
RazorPageFactory = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="RazorPageFactoryResult"/> with the
|
||||||
|
/// specified <see cref="IRazorPage"/> factory.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="razorPageFactory">The <see cref="IRazorPage"/> factory.</param>
|
||||||
|
/// <param name="expirationTokens">One or more <see cref="IChangeToken"/> instances.</param>
|
||||||
|
public RazorPageFactoryResult(
|
||||||
|
Func<IRazorPage> razorPageFactory,
|
||||||
|
IList<IChangeToken> expirationTokens)
|
||||||
|
{
|
||||||
|
if (razorPageFactory == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(razorPageFactory));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expirationTokens == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(expirationTokens));
|
||||||
|
}
|
||||||
|
|
||||||
|
RazorPageFactory = razorPageFactory;
|
||||||
|
ExpirationTokens = expirationTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="IRazorPage"/> factory.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This property is <c>null</c> when <see cref="Success"/> is <c>false</c>.</remarks>
|
||||||
|
public Func<IRazorPage> RazorPageFactory { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// One or more <see cref="IChangeToken"/>s associated with this instance of
|
||||||
|
/// <see cref="RazorPageFactoryResult"/>.
|
||||||
|
/// </summary>
|
||||||
|
public IList<IChangeToken> ExpirationTokens { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value that determines if the page was successfully located.
|
||||||
|
/// </summary>
|
||||||
|
public bool Success => RazorPageFactory != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,9 +7,9 @@ using System.Collections.Generic;
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the results of locating a <see cref="IRazorPage"/>.
|
/// Result of locating a <see cref="IRazorPage"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RazorPageResult
|
public struct RazorPageResult
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of <see cref="RazorPageResult"/> for a successful discovery.
|
/// Initializes a new instance of <see cref="RazorPageResult"/> for a successful discovery.
|
||||||
|
|
@ -30,6 +30,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
Name = name;
|
Name = name;
|
||||||
Page = page;
|
Page = page;
|
||||||
|
SearchedLocations = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -50,6 +51,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
}
|
}
|
||||||
|
|
||||||
Name = name;
|
Name = name;
|
||||||
|
Page = null;
|
||||||
SearchedLocations = searchedLocations;
|
SearchedLocations = searchedLocations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
private readonly IRazorViewEngine _viewEngine;
|
private readonly IRazorViewEngine _viewEngine;
|
||||||
private readonly IRazorPageActivator _pageActivator;
|
private readonly IRazorPageActivator _pageActivator;
|
||||||
private readonly IViewStartProvider _viewStartProvider;
|
|
||||||
private readonly HtmlEncoder _htmlEncoder;
|
private readonly HtmlEncoder _htmlEncoder;
|
||||||
private IPageExecutionListenerFeature _pageExecutionFeature;
|
private IPageExecutionListenerFeature _pageExecutionFeature;
|
||||||
|
|
||||||
|
|
@ -31,22 +30,22 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="viewEngine">The <see cref="IRazorViewEngine"/> used to locate Layout pages.</param>
|
/// <param name="viewEngine">The <see cref="IRazorViewEngine"/> used to locate Layout pages.</param>
|
||||||
/// <param name="pageActivator">The <see cref="IRazorPageActivator"/> used to activate pages.</param>
|
/// <param name="pageActivator">The <see cref="IRazorPageActivator"/> used to activate pages.</param>
|
||||||
/// <param name="viewStartProvider">The <see cref="IViewStartProvider"/> used for discovery of _ViewStart
|
/// <param name="viewStartPages">The sequence of <see cref="IRazorPage" /> instances executed as _ViewStarts.
|
||||||
|
/// </param>
|
||||||
/// <param name="razorPage">The <see cref="IRazorPage"/> instance to execute.</param>
|
/// <param name="razorPage">The <see cref="IRazorPage"/> instance to execute.</param>
|
||||||
/// <param name="htmlEncoder">The HTML encoder.</param>
|
/// <param name="htmlEncoder">The HTML encoder.</param>
|
||||||
/// <param name="isPartial">Determines if the view is to be executed as a partial.</param>
|
/// <param name="isPartial">Determines if the view is to be executed as a partial.</param>
|
||||||
/// pages</param>
|
|
||||||
public RazorView(
|
public RazorView(
|
||||||
IRazorViewEngine viewEngine,
|
IRazorViewEngine viewEngine,
|
||||||
IRazorPageActivator pageActivator,
|
IRazorPageActivator pageActivator,
|
||||||
IViewStartProvider viewStartProvider,
|
IReadOnlyList<IRazorPage> viewStartPages,
|
||||||
IRazorPage razorPage,
|
IRazorPage razorPage,
|
||||||
HtmlEncoder htmlEncoder,
|
HtmlEncoder htmlEncoder,
|
||||||
bool isPartial)
|
bool isPartial)
|
||||||
{
|
{
|
||||||
_viewEngine = viewEngine;
|
_viewEngine = viewEngine;
|
||||||
_pageActivator = pageActivator;
|
_pageActivator = pageActivator;
|
||||||
_viewStartProvider = viewStartProvider;
|
ViewStartPages = viewStartPages;
|
||||||
RazorPage = razorPage;
|
RazorPage = razorPage;
|
||||||
_htmlEncoder = htmlEncoder;
|
_htmlEncoder = htmlEncoder;
|
||||||
IsPartial = isPartial;
|
IsPartial = isPartial;
|
||||||
|
|
@ -68,6 +67,12 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsPartial { get; }
|
public bool IsPartial { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the sequence of _ViewStart <see cref="IRazorPage"/> instances
|
||||||
|
/// that are executed by this view if <see cref="IsPartial"/> is <c>false</c>.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<IRazorPage> ViewStartPages { get; }
|
||||||
|
|
||||||
private bool EnableInstrumentation
|
private bool EnableInstrumentation
|
||||||
{
|
{
|
||||||
get { return _pageExecutionFeature != null; }
|
get { return _pageExecutionFeature != null; }
|
||||||
|
|
@ -153,14 +158,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
private async Task RenderViewStartAsync(ViewContext context)
|
private async Task RenderViewStartAsync(ViewContext context)
|
||||||
{
|
{
|
||||||
var viewStarts = _viewStartProvider.GetViewStartPages(RazorPage.Path);
|
|
||||||
|
|
||||||
string layout = null;
|
string layout = null;
|
||||||
var oldFilePath = context.ExecutingFilePath;
|
var oldFilePath = context.ExecutingFilePath;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var viewStart in viewStarts)
|
for (var i = 0; i < ViewStartPages.Count; i++)
|
||||||
{
|
{
|
||||||
|
var viewStart = ViewStartPages[i];
|
||||||
context.ExecutingFilePath = viewStart.Path;
|
context.ExecutingFilePath = viewStart.Path;
|
||||||
// Copy the layout value from the previous view start (if any) to the current.
|
// Copy the layout value from the previous view start (if any) to the current.
|
||||||
viewStart.Layout = layout;
|
viewStart.Layout = layout;
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,12 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Text.Encodings.Web;
|
||||||
using Microsoft.AspNet.Mvc.Routing;
|
using Microsoft.AspNet.Mvc.Routing;
|
||||||
using Microsoft.AspNet.Mvc.ViewEngines;
|
using Microsoft.AspNet.Mvc.ViewEngines;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.OptionsModel;
|
using Microsoft.Extensions.OptionsModel;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
|
|
@ -22,41 +24,34 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
public class RazorViewEngine : IRazorViewEngine
|
public class RazorViewEngine : IRazorViewEngine
|
||||||
{
|
{
|
||||||
private const string ViewExtension = ".cshtml";
|
private const string ViewExtension = ".cshtml";
|
||||||
internal const string ControllerKey = "controller";
|
private const string ControllerKey = "controller";
|
||||||
internal const string AreaKey = "area";
|
private const string AreaKey = "area";
|
||||||
|
private static readonly ViewLocationCacheItem[] EmptyViewStartLocationCacheItems =
|
||||||
|
new ViewLocationCacheItem[0];
|
||||||
|
private static readonly TimeSpan _cacheExpirationDuration = TimeSpan.FromMinutes(20);
|
||||||
|
|
||||||
private static readonly IEnumerable<string> _viewLocationFormats = new[]
|
private readonly IRazorPageFactoryProvider _pageFactory;
|
||||||
{
|
|
||||||
"/Views/{1}/{0}" + ViewExtension,
|
|
||||||
"/Views/Shared/{0}" + ViewExtension,
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly IEnumerable<string> _areaViewLocationFormats = new[]
|
|
||||||
{
|
|
||||||
"/Areas/{2}/Views/{1}/{0}" + ViewExtension,
|
|
||||||
"/Areas/{2}/Views/Shared/{0}" + ViewExtension,
|
|
||||||
"/Views/Shared/{0}" + ViewExtension,
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly IRazorPageFactory _pageFactory;
|
|
||||||
private readonly IRazorViewFactory _viewFactory;
|
|
||||||
private readonly IList<IViewLocationExpander> _viewLocationExpanders;
|
private readonly IList<IViewLocationExpander> _viewLocationExpanders;
|
||||||
private readonly IViewLocationCache _viewLocationCache;
|
private readonly IRazorPageActivator _pageActivator;
|
||||||
|
private readonly HtmlEncoder _htmlEncoder;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="RazorViewEngine" /> class.
|
/// Initializes a new instance of the <see cref="RazorViewEngine" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pageFactory">The page factory used for creating <see cref="IRazorPage"/> instances.</param>
|
|
||||||
public RazorViewEngine(
|
public RazorViewEngine(
|
||||||
IRazorPageFactory pageFactory,
|
IRazorPageFactoryProvider pageFactory,
|
||||||
IRazorViewFactory viewFactory,
|
IRazorPageActivator pageActivator,
|
||||||
IOptions<RazorViewEngineOptions> optionsAccessor,
|
HtmlEncoder htmlEncoder,
|
||||||
IViewLocationCache viewLocationCache)
|
IOptions<RazorViewEngineOptions> optionsAccessor)
|
||||||
{
|
{
|
||||||
_pageFactory = pageFactory;
|
_pageFactory = pageFactory;
|
||||||
_viewFactory = viewFactory;
|
_pageActivator = pageActivator;
|
||||||
_viewLocationExpanders = optionsAccessor.Value.ViewLocationExpanders;
|
_viewLocationExpanders = optionsAccessor.Value.ViewLocationExpanders;
|
||||||
_viewLocationCache = viewLocationCache;
|
_htmlEncoder = htmlEncoder;
|
||||||
|
ViewLookupCache = new MemoryCache(new MemoryCacheOptions
|
||||||
|
{
|
||||||
|
CompactOnMemoryPressure = false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -72,10 +67,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// For example, the view for the <c>Test</c> action of <c>HomeController</c> should be located at
|
/// For example, the view for the <c>Test</c> action of <c>HomeController</c> should be located at
|
||||||
/// <c>/Views/Home/Test.cshtml</c>. Locations such as <c>/views/home/test.cshtml</c> would not be discovered
|
/// <c>/Views/Home/Test.cshtml</c>. Locations such as <c>/views/home/test.cshtml</c> would not be discovered
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public virtual IEnumerable<string> ViewLocationFormats
|
public virtual IEnumerable<string> ViewLocationFormats { get; } = new[]
|
||||||
{
|
{
|
||||||
get { return _viewLocationFormats; }
|
"/Views/{1}/{0}" + ViewExtension,
|
||||||
}
|
"/Views/Shared/{0}" + ViewExtension,
|
||||||
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the locations where this instance of <see cref="RazorViewEngine"/> will search for views within an
|
/// Gets the locations where this instance of <see cref="RazorViewEngine"/> will search for views within an
|
||||||
|
|
@ -92,15 +88,20 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// For example, the view for the <c>Test</c> action of <c>HomeController</c> should be located at
|
/// For example, the view for the <c>Test</c> action of <c>HomeController</c> should be located at
|
||||||
/// <c>/Views/Home/Test.cshtml</c>. Locations such as <c>/views/home/test.cshtml</c> would not be discovered
|
/// <c>/Views/Home/Test.cshtml</c>. Locations such as <c>/views/home/test.cshtml</c> would not be discovered
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public virtual IEnumerable<string> AreaViewLocationFormats
|
public virtual IEnumerable<string> AreaViewLocationFormats { get; } = new[]
|
||||||
{
|
{
|
||||||
get { return _areaViewLocationFormats; }
|
"/Areas/{2}/Views/{1}/{0}" + ViewExtension,
|
||||||
}
|
"/Areas/{2}/Views/Shared/{0}" + ViewExtension,
|
||||||
|
"/Views/Shared/{0}" + ViewExtension,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A cache for results of view lookups.
|
||||||
|
/// </summary>
|
||||||
|
protected IMemoryCache ViewLookupCache { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ViewEngineResult FindView(
|
public ViewEngineResult FindView(ActionContext context, string viewName)
|
||||||
ActionContext context,
|
|
||||||
string viewName)
|
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
|
|
@ -112,14 +113,12 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(viewName));
|
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(viewName));
|
||||||
}
|
}
|
||||||
|
|
||||||
var pageResult = GetRazorPageResult(context, viewName, isPartial: false);
|
var pageResult = GetViewLocationCacheResult(context, viewName, isPartial: false);
|
||||||
return CreateViewEngineResult(pageResult, _viewFactory, isPartial: false);
|
return CreateViewEngineResult(pageResult, viewName, isPartial: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ViewEngineResult FindPartialView(
|
public ViewEngineResult FindPartialView(ActionContext context, string partialViewName)
|
||||||
ActionContext context,
|
|
||||||
string partialViewName)
|
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
|
|
@ -131,14 +130,12 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(partialViewName));
|
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(partialViewName));
|
||||||
}
|
}
|
||||||
|
|
||||||
var pageResult = GetRazorPageResult(context, partialViewName, isPartial: true);
|
var pageResult = GetViewLocationCacheResult(context, partialViewName, isPartial: true);
|
||||||
return CreateViewEngineResult(pageResult, _viewFactory, isPartial: true);
|
return CreateViewEngineResult(pageResult, partialViewName, isPartial: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public RazorPageResult FindPage(
|
public RazorPageResult FindPage(ActionContext context, string pageName)
|
||||||
ActionContext context,
|
|
||||||
string pageName)
|
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
|
|
@ -150,7 +147,16 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(pageName));
|
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(pageName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetRazorPageResult(context, pageName, isPartial: true);
|
var cacheResult = GetViewLocationCacheResult(context, pageName, isPartial: true);
|
||||||
|
if (cacheResult.Success)
|
||||||
|
{
|
||||||
|
var razorPage = cacheResult.ViewEntry.PageFactory();
|
||||||
|
return new RazorPageResult(pageName, razorPage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new RazorPageResult(pageName, cacheResult.SearchedLocations);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -166,9 +172,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// <see cref="Abstractions.ActionDescriptor.RouteConstraints"/> for traditional routes to get route values
|
/// <see cref="Abstractions.ActionDescriptor.RouteConstraints"/> for traditional routes to get route values
|
||||||
/// produces consistently cased results.
|
/// produces consistently cased results.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static string GetNormalizedRouteValue(
|
public static string GetNormalizedRouteValue(ActionContext context, string key)
|
||||||
ActionContext context,
|
|
||||||
string key)
|
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
|
|
@ -228,26 +232,14 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
return stringRouteValue;
|
return stringRouteValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private RazorPageResult GetRazorPageResult(
|
private ViewLocationCacheResult GetViewLocationCacheResult(
|
||||||
ActionContext context,
|
ActionContext context,
|
||||||
string pageName,
|
string pageName,
|
||||||
bool isPartial)
|
bool isPartial)
|
||||||
{
|
{
|
||||||
if (IsApplicationRelativePath(pageName))
|
if (IsApplicationRelativePath(pageName))
|
||||||
{
|
{
|
||||||
var applicationRelativePath = pageName;
|
return LocatePageFromPath(pageName, isPartial);
|
||||||
if (!pageName.EndsWith(ViewExtension, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
applicationRelativePath += ViewExtension;
|
|
||||||
}
|
|
||||||
|
|
||||||
var page = _pageFactory.CreateInstance(applicationRelativePath);
|
|
||||||
if (page != null)
|
|
||||||
{
|
|
||||||
return new RazorPageResult(pageName, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new RazorPageResult(pageName, new[] { pageName });
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -255,105 +247,221 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private RazorPageResult LocatePageFromViewLocations(
|
private ViewLocationCacheResult LocatePageFromPath(string pageName, bool isPartial)
|
||||||
ActionContext context,
|
{
|
||||||
|
var applicationRelativePath = pageName;
|
||||||
|
if (!pageName.EndsWith(ViewExtension, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
applicationRelativePath += ViewExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cacheKey = new ViewLocationCacheKey(applicationRelativePath, isPartial);
|
||||||
|
ViewLocationCacheResult cacheResult;
|
||||||
|
if (!ViewLookupCache.TryGetValue(cacheKey, out cacheResult))
|
||||||
|
{
|
||||||
|
var expirationTokens = new HashSet<IChangeToken>();
|
||||||
|
cacheResult = CreateCacheResult(cacheKey, expirationTokens, applicationRelativePath, isPartial);
|
||||||
|
|
||||||
|
var cacheEntryOptions = new MemoryCacheEntryOptions();
|
||||||
|
cacheEntryOptions.SetSlidingExpiration(_cacheExpirationDuration);
|
||||||
|
foreach (var expirationToken in expirationTokens)
|
||||||
|
{
|
||||||
|
cacheEntryOptions.AddExpirationToken(expirationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No views were found at the specified location. Create a not found result.
|
||||||
|
if (cacheResult == null)
|
||||||
|
{
|
||||||
|
cacheResult = new ViewLocationCacheResult(new[] { pageName });
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheResult = ViewLookupCache.Set<ViewLocationCacheResult>(
|
||||||
|
cacheKey,
|
||||||
|
cacheResult,
|
||||||
|
cacheEntryOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cacheResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ViewLocationCacheResult LocatePageFromViewLocations(
|
||||||
|
ActionContext actionContext,
|
||||||
string pageName,
|
string pageName,
|
||||||
bool isPartial)
|
bool isPartial)
|
||||||
{
|
{
|
||||||
// Initialize the dictionary for the typical case of having controller and action tokens.
|
var controllerName = GetNormalizedRouteValue(actionContext, ControllerKey);
|
||||||
var areaName = GetNormalizedRouteValue(context, AreaKey);
|
var areaName = GetNormalizedRouteValue(actionContext, AreaKey);
|
||||||
|
var expanderContext = new ViewLocationExpanderContext(
|
||||||
|
actionContext,
|
||||||
|
pageName,
|
||||||
|
controllerName,
|
||||||
|
areaName,
|
||||||
|
isPartial);
|
||||||
|
Dictionary<string, string> expanderValues = null;
|
||||||
|
|
||||||
// Only use the area view location formats if we have an area token.
|
|
||||||
var viewLocations = !string.IsNullOrEmpty(areaName) ? AreaViewLocationFormats :
|
|
||||||
ViewLocationFormats;
|
|
||||||
|
|
||||||
var expanderContext = new ViewLocationExpanderContext(context, pageName, isPartial);
|
|
||||||
if (_viewLocationExpanders.Count > 0)
|
if (_viewLocationExpanders.Count > 0)
|
||||||
{
|
{
|
||||||
expanderContext.Values = new Dictionary<string, string>(StringComparer.Ordinal);
|
expanderValues = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||||
|
expanderContext.Values = expanderValues;
|
||||||
|
|
||||||
// 1. Populate values from viewLocationExpanders.
|
|
||||||
// Perf: Avoid allocations
|
// Perf: Avoid allocations
|
||||||
for( var i = 0; i < _viewLocationExpanders.Count; i++)
|
for (var i = 0; i < _viewLocationExpanders.Count; i++)
|
||||||
{
|
{
|
||||||
_viewLocationExpanders[i].PopulateValues(expanderContext);
|
_viewLocationExpanders[i].PopulateValues(expanderContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. With the values that we've accumumlated so far, check if we have a cached result.
|
var cacheKey = new ViewLocationCacheKey(
|
||||||
IEnumerable<string> locationsToSearch = null;
|
expanderContext.ViewName,
|
||||||
var cachedResult = _viewLocationCache.Get(expanderContext);
|
expanderContext.ControllerName,
|
||||||
if (!cachedResult.Equals(ViewLocationCacheResult.None))
|
expanderContext.ViewName,
|
||||||
{
|
expanderContext.IsPartial,
|
||||||
if (cachedResult.IsFoundResult)
|
expanderValues);
|
||||||
{
|
|
||||||
var page = _pageFactory.CreateInstance(cachedResult.ViewLocation);
|
|
||||||
|
|
||||||
if (page != null)
|
ViewLocationCacheResult cacheResult;
|
||||||
{
|
if (!ViewLookupCache.TryGetValue(cacheKey, out cacheResult))
|
||||||
// 2a We have a cache entry where a view was previously found.
|
{
|
||||||
return new RazorPageResult(pageName, page);
|
cacheResult = OnCacheMiss(expanderContext, cacheKey);
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
locationsToSearch = cachedResult.SearchedLocations;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (locationsToSearch == null)
|
return cacheResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ViewLocationCacheResult OnCacheMiss(
|
||||||
|
ViewLocationExpanderContext expanderContext,
|
||||||
|
ViewLocationCacheKey cacheKey)
|
||||||
|
{
|
||||||
|
// Only use the area view location formats if we have an area token.
|
||||||
|
var viewLocations = !string.IsNullOrEmpty(expanderContext.AreaName) ?
|
||||||
|
AreaViewLocationFormats :
|
||||||
|
ViewLocationFormats;
|
||||||
|
|
||||||
|
for (var i = 0; i < _viewLocationExpanders.Count; i++)
|
||||||
{
|
{
|
||||||
// 2b. We did not find a cached location or did not find a IRazorPage at the cached location.
|
viewLocations = _viewLocationExpanders[i].ExpandViewLocations(expanderContext, viewLocations);
|
||||||
// The cached value has expired and we need to look up the page.
|
|
||||||
foreach (var expander in _viewLocationExpanders)
|
|
||||||
{
|
|
||||||
viewLocations = expander.ExpandViewLocations(expanderContext, viewLocations);
|
|
||||||
}
|
|
||||||
|
|
||||||
var controllerName = GetNormalizedRouteValue(context, ControllerKey);
|
|
||||||
|
|
||||||
locationsToSearch = viewLocations.Select(
|
|
||||||
location => string.Format(
|
|
||||||
CultureInfo.InvariantCulture,
|
|
||||||
location,
|
|
||||||
pageName,
|
|
||||||
controllerName,
|
|
||||||
areaName
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Use the expanded locations to look up a page.
|
ViewLocationCacheResult cacheResult = null;
|
||||||
var searchedLocations = new List<string>();
|
var searchedLocations = new List<string>();
|
||||||
foreach (var path in locationsToSearch)
|
var expirationTokens = new HashSet<IChangeToken>();
|
||||||
|
foreach (var location in viewLocations)
|
||||||
{
|
{
|
||||||
var page = _pageFactory.CreateInstance(path);
|
var path = string.Format(
|
||||||
if (page != null)
|
CultureInfo.InvariantCulture,
|
||||||
|
location,
|
||||||
|
expanderContext.ViewName,
|
||||||
|
expanderContext.ControllerName,
|
||||||
|
expanderContext.AreaName);
|
||||||
|
|
||||||
|
cacheResult = CreateCacheResult(cacheKey, expirationTokens, path, expanderContext.IsPartial);
|
||||||
|
if (cacheResult != null)
|
||||||
{
|
{
|
||||||
// 3a. We found a page. Cache the set of values that produced it and return a found result.
|
break;
|
||||||
_viewLocationCache.Set(expanderContext, new ViewLocationCacheResult(path, searchedLocations));
|
|
||||||
return new RazorPageResult(pageName, page);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
searchedLocations.Add(path);
|
searchedLocations.Add(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3b. We did not find a page for any of the paths.
|
// No views were found at the specified location. Create a not found result.
|
||||||
_viewLocationCache.Set(expanderContext, new ViewLocationCacheResult(searchedLocations));
|
if (cacheResult == null)
|
||||||
return new RazorPageResult(pageName, searchedLocations);
|
{
|
||||||
|
cacheResult = new ViewLocationCacheResult(searchedLocations);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cacheEntryOptions = new MemoryCacheEntryOptions();
|
||||||
|
cacheEntryOptions.SetSlidingExpiration(_cacheExpirationDuration);
|
||||||
|
foreach (var expirationToken in expirationTokens)
|
||||||
|
{
|
||||||
|
cacheEntryOptions.AddExpirationToken(expirationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ViewLookupCache.Set<ViewLocationCacheResult>(cacheKey, cacheResult, cacheEntryOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ViewLocationCacheResult CreateCacheResult(
|
||||||
|
ViewLocationCacheKey cacheKey,
|
||||||
|
HashSet<IChangeToken> expirationTokens,
|
||||||
|
string relativePath,
|
||||||
|
bool isPartial)
|
||||||
|
{
|
||||||
|
var factoryResult = _pageFactory.CreateFactory(relativePath);
|
||||||
|
if (factoryResult.ExpirationTokens != null)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < factoryResult.ExpirationTokens.Count; i++)
|
||||||
|
{
|
||||||
|
expirationTokens.Add(factoryResult.ExpirationTokens[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (factoryResult.Success)
|
||||||
|
{
|
||||||
|
// Don't need to lookup _ViewStarts for partials.
|
||||||
|
var viewStartPages = isPartial ?
|
||||||
|
EmptyViewStartLocationCacheItems :
|
||||||
|
GetViewStartPages(relativePath, expirationTokens);
|
||||||
|
|
||||||
|
return new ViewLocationCacheResult(
|
||||||
|
new ViewLocationCacheItem(factoryResult.RazorPageFactory, relativePath),
|
||||||
|
viewStartPages);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IReadOnlyList<ViewLocationCacheItem> GetViewStartPages(
|
||||||
|
string path,
|
||||||
|
HashSet<IChangeToken> expirationTokens)
|
||||||
|
{
|
||||||
|
var viewStartPages = new List<ViewLocationCacheItem>();
|
||||||
|
foreach (var viewStartPath in ViewHierarchyUtility.GetViewStartLocations(path))
|
||||||
|
{
|
||||||
|
var result = _pageFactory.CreateFactory(viewStartPath);
|
||||||
|
if (result.ExpirationTokens != null)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < result.ExpirationTokens.Count; i++)
|
||||||
|
{
|
||||||
|
expirationTokens.Add(result.ExpirationTokens[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.Success)
|
||||||
|
{
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return viewStartPages;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ViewEngineResult CreateViewEngineResult(
|
private ViewEngineResult CreateViewEngineResult(
|
||||||
RazorPageResult result,
|
ViewLocationCacheResult result,
|
||||||
IRazorViewFactory razorViewFactory,
|
string viewName,
|
||||||
bool isPartial)
|
bool isPartial)
|
||||||
{
|
{
|
||||||
if (result.SearchedLocations != null)
|
if (!result.Success)
|
||||||
{
|
{
|
||||||
return ViewEngineResult.NotFound(result.Name, result.SearchedLocations);
|
return ViewEngineResult.NotFound(viewName, result.SearchedLocations);
|
||||||
}
|
}
|
||||||
|
|
||||||
var view = razorViewFactory.GetView(this, result.Page, isPartial);
|
var page = result.ViewEntry.PageFactory();
|
||||||
return ViewEngineResult.Found(result.Name, view);
|
var viewStarts = new IRazorPage[result.ViewStartEntries.Count];
|
||||||
|
for (var i = 0; i < viewStarts.Length; i++)
|
||||||
|
{
|
||||||
|
var viewStartItem = result.ViewStartEntries[i];
|
||||||
|
viewStarts[i] = result.ViewStartEntries[i].PageFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
var view = new RazorView(
|
||||||
|
this,
|
||||||
|
_pageActivator,
|
||||||
|
viewStarts,
|
||||||
|
page,
|
||||||
|
_htmlEncoder,
|
||||||
|
isPartial);
|
||||||
|
return ViewEngineResult.Found(viewName, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsApplicationRelativePath(string name)
|
private static bool IsApplicationRelativePath(string name)
|
||||||
|
|
|
||||||
|
|
@ -1,62 +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.Text.Encodings.Web;
|
|
||||||
using Microsoft.AspNet.Mvc.ViewEngines;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the default <see cref="IRazorViewFactory"/> implementation that creates
|
|
||||||
/// <see cref="RazorView"/> instances with a given <see cref="IRazorPage"/>.
|
|
||||||
/// </summary>
|
|
||||||
public class RazorViewFactory : IRazorViewFactory
|
|
||||||
{
|
|
||||||
private readonly HtmlEncoder _htmlEncoder;
|
|
||||||
private readonly IRazorPageActivator _pageActivator;
|
|
||||||
private readonly IViewStartProvider _viewStartProvider;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of RazorViewFactory
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pageActivator">The <see cref="IRazorPageActivator"/> used to activate pages.</param>
|
|
||||||
/// <param name="viewStartProvider">The <see cref="IViewStartProvider"/> used for discovery of _ViewStart
|
|
||||||
/// pages</param>
|
|
||||||
public RazorViewFactory(
|
|
||||||
IRazorPageActivator pageActivator,
|
|
||||||
IViewStartProvider viewStartProvider,
|
|
||||||
HtmlEncoder htmlEncoder)
|
|
||||||
{
|
|
||||||
_pageActivator = pageActivator;
|
|
||||||
_viewStartProvider = viewStartProvider;
|
|
||||||
_htmlEncoder = htmlEncoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IView GetView(
|
|
||||||
IRazorViewEngine viewEngine,
|
|
||||||
IRazorPage page,
|
|
||||||
bool isPartial)
|
|
||||||
{
|
|
||||||
if (viewEngine == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(viewEngine));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(page));
|
|
||||||
}
|
|
||||||
|
|
||||||
var razorView = new RazorView(
|
|
||||||
viewEngine,
|
|
||||||
_pageActivator,
|
|
||||||
_viewStartProvider,
|
|
||||||
page,
|
|
||||||
_htmlEncoder,
|
|
||||||
isPartial);
|
|
||||||
return razorView;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An item in <see cref="ViewLocationCacheResult"/>.
|
||||||
|
/// </summary>
|
||||||
|
public struct ViewLocationCacheItem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="ViewLocationCacheItem"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="razorPageFactory">The <see cref="IRazorPage"/> factory.</param>
|
||||||
|
/// <param name="location">The application relative path of the <see cref="IRazorPage"/>.</param>
|
||||||
|
public ViewLocationCacheItem(Func<IRazorPage> razorPageFactory, string location)
|
||||||
|
{
|
||||||
|
PageFactory = razorPageFactory;
|
||||||
|
Location = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the application relative path of the <see cref="IRazorPage"/>
|
||||||
|
/// </summary>
|
||||||
|
public string Location { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="IRazorPage"/> factory.
|
||||||
|
/// </summary>
|
||||||
|
public Func<IRazorPage> PageFactory { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
// 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.Extensions.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Key for entries in <see cref="RazorViewEngine.ViewLookupCache"/>.
|
||||||
|
/// </summary>
|
||||||
|
public struct ViewLocationCacheKey : IEquatable<ViewLocationCacheKey>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="ViewLocationCacheKey"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="viewName">The view name or path.</param>
|
||||||
|
/// <param name="isPartial">Determines if the view is a partial.</param>
|
||||||
|
public ViewLocationCacheKey(
|
||||||
|
string viewName,
|
||||||
|
bool isPartial)
|
||||||
|
: this(
|
||||||
|
viewName,
|
||||||
|
controllerName: null,
|
||||||
|
areaName: null,
|
||||||
|
isPartial: isPartial,
|
||||||
|
values: null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of <see cref="ViewLocationCacheKey"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="viewName">The view name.</param>
|
||||||
|
/// <param name="controllerName">The controller name.</param>
|
||||||
|
/// <param name="areaName">The area name.</param>
|
||||||
|
/// <param name="isPartial">Determines if the view is a partial.</param>
|
||||||
|
/// <param name="values">Values from <see cref="IViewLocationExpander"/> instances.</param>
|
||||||
|
public ViewLocationCacheKey(
|
||||||
|
string viewName,
|
||||||
|
string controllerName,
|
||||||
|
string areaName,
|
||||||
|
bool isPartial,
|
||||||
|
IReadOnlyDictionary<string, string> values)
|
||||||
|
{
|
||||||
|
ViewName = viewName;
|
||||||
|
ControllerName = controllerName;
|
||||||
|
AreaName = areaName;
|
||||||
|
IsPartial = isPartial;
|
||||||
|
ViewLocationExpanderValues = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the view name.
|
||||||
|
/// </summary>
|
||||||
|
public string ViewName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the controller name.
|
||||||
|
/// </summary>
|
||||||
|
public string ControllerName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the area name.
|
||||||
|
/// </summary>
|
||||||
|
public string AreaName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if the view is a partial.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPartial { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the values populated by <see cref="IViewLocationExpander"/> instances.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyDictionary<string, string> ViewLocationExpanderValues { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Equals(ViewLocationCacheKey y)
|
||||||
|
{
|
||||||
|
if (IsPartial != y.IsPartial ||
|
||||||
|
!string.Equals(ViewName, y.ViewName, StringComparison.Ordinal) ||
|
||||||
|
!string.Equals(ControllerName, y.ControllerName, StringComparison.Ordinal) ||
|
||||||
|
!string.Equals(AreaName, y.AreaName, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ReferenceEquals(ViewLocationExpanderValues, y.ViewLocationExpanderValues))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ViewLocationExpanderValues == null ||
|
||||||
|
y.ViewLocationExpanderValues == null ||
|
||||||
|
(ViewLocationExpanderValues.Count != y.ViewLocationExpanderValues.Count))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var item in ViewLocationExpanderValues)
|
||||||
|
{
|
||||||
|
string yValue;
|
||||||
|
if (!y.ViewLocationExpanderValues.TryGetValue(item.Key, out yValue) ||
|
||||||
|
!string.Equals(item.Value, yValue, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
if (obj is ViewLocationCacheKey)
|
||||||
|
{
|
||||||
|
return Equals((ViewLocationCacheKey)obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
var hashCodeCombiner = HashCodeCombiner.Start();
|
||||||
|
hashCodeCombiner.Add(IsPartial ? 1 : 0);
|
||||||
|
hashCodeCombiner.Add(ViewName, StringComparer.Ordinal);
|
||||||
|
hashCodeCombiner.Add(ControllerName, StringComparer.Ordinal);
|
||||||
|
hashCodeCombiner.Add(AreaName, StringComparer.Ordinal);
|
||||||
|
|
||||||
|
if (ViewLocationExpanderValues != null)
|
||||||
|
{
|
||||||
|
foreach (var item in ViewLocationExpanderValues)
|
||||||
|
{
|
||||||
|
hashCodeCombiner.Add(item.Key, StringComparer.Ordinal);
|
||||||
|
hashCodeCombiner.Add(item.Value, StringComparer.Ordinal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashCodeCombiner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,36 +3,32 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Result of <see cref="IViewLocationCache"/> lookups.
|
/// Result of view location cache lookup.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct ViewLocationCacheResult : IEquatable<ViewLocationCacheResult>
|
public class ViewLocationCacheResult
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of <see cref="ViewLocationCacheResult"/>
|
/// Initializes a new instance of <see cref="ViewLocationCacheResult"/>
|
||||||
/// for a view that was successfully found at the specified location.
|
/// for a view that was successfully found at the specified location.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="foundLocation">The view location.</param>
|
/// <param name="view">The <see cref="ViewLocationCacheItem"/> for the found view.</param>
|
||||||
/// <param name="searchedLocations">Locations that were searched
|
/// <param name="viewStarts"><see cref="ViewLocationCacheItem"/>s for applicable _ViewStarts.</param>
|
||||||
/// in addition to <paramref name="foundLocation"/>.</param>
|
|
||||||
public ViewLocationCacheResult(
|
public ViewLocationCacheResult(
|
||||||
string foundLocation,
|
ViewLocationCacheItem view,
|
||||||
IEnumerable<string> searchedLocations)
|
IReadOnlyList<ViewLocationCacheItem> viewStarts)
|
||||||
: this(searchedLocations)
|
|
||||||
{
|
{
|
||||||
if (foundLocation == null)
|
if (viewStarts == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(foundLocation));
|
throw new ArgumentNullException(nameof(viewStarts));
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewLocation = foundLocation;
|
ViewEntry = view;
|
||||||
SearchedLocations = searchedLocations;
|
ViewStartEntries = viewStarts;
|
||||||
IsFoundResult = true;
|
Success = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -48,27 +44,26 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchedLocations = searchedLocations;
|
SearchedLocations = searchedLocations;
|
||||||
ViewLocation = null;
|
|
||||||
IsFoundResult = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A <see cref="ViewLocationCacheResult"/> that represents a cache miss.
|
/// <see cref="ViewLocationCacheItem"/> for the located view.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly ViewLocationCacheResult None = new ViewLocationCacheResult(Enumerable.Empty<string>());
|
/// <remarks><c>null</c> if <see cref="Success"/> is <c>false</c>.</remarks>
|
||||||
|
public ViewLocationCacheItem ViewEntry { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The location the view was found.
|
/// <see cref="ViewLocationCacheItem"/>s for applicable _ViewStarts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>This is available if <see cref="IsFoundResult"/> is <c>true</c>.</remarks>
|
/// <remarks><c>null</c> if <see cref="Success"/> is <c>false</c>.</remarks>
|
||||||
public string ViewLocation { get; }
|
public IReadOnlyList<ViewLocationCacheItem> ViewStartEntries { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The sequence of locations that were searched.
|
/// The sequence of locations that were searched.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// When <see cref="IsFoundResult"/> is <c>true</c> this includes all paths that were search prior to finding
|
/// When <see cref="Success"/> is <c>true</c> this includes all paths that were search prior to finding
|
||||||
/// a view at <see cref="ViewLocation"/>. When <see cref="IsFoundResult"/> is <c>false</c>, this includes
|
/// a view at <see cref="ViewEntry"/>. When <see cref="Success"/> is <c>false</c>, this includes
|
||||||
/// all search paths.
|
/// all search paths.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public IEnumerable<string> SearchedLocations { get; }
|
public IEnumerable<string> SearchedLocations { get; }
|
||||||
|
|
@ -76,55 +71,6 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value that indicates whether the view was successfully found.
|
/// Gets a value that indicates whether the view was successfully found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsFoundResult { get; }
|
public bool Success { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool Equals(ViewLocationCacheResult other)
|
|
||||||
{
|
|
||||||
if (IsFoundResult != other.IsFoundResult)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsFoundResult)
|
|
||||||
{
|
|
||||||
return string.Equals(ViewLocation, other.ViewLocation, StringComparison.Ordinal);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (SearchedLocations == other.SearchedLocations)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SearchedLocations == null || other.SearchedLocations == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Enumerable.SequenceEqual(SearchedLocations, other.SearchedLocations, StringComparer.Ordinal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
var hashCodeCombiner = HashCodeCombiner.Start();
|
|
||||||
hashCodeCombiner.Add(IsFoundResult);
|
|
||||||
|
|
||||||
if (IsFoundResult)
|
|
||||||
{
|
|
||||||
hashCodeCombiner.Add(ViewLocation, StringComparer.Ordinal);
|
|
||||||
}
|
|
||||||
else if (SearchedLocations != null)
|
|
||||||
{
|
|
||||||
foreach (var location in SearchedLocations)
|
|
||||||
{
|
|
||||||
hashCodeCombiner.Add(location, StringComparer.Ordinal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashCodeCombiner;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,14 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="actionContext">The <see cref="Mvc.ActionContext"/> for the current executing action.</param>
|
/// <param name="actionContext">The <see cref="Mvc.ActionContext"/> for the current executing action.</param>
|
||||||
/// <param name="viewName">The view name.</param>
|
/// <param name="viewName">The view name.</param>
|
||||||
|
/// <param name="controllerName">The controller name.</param>
|
||||||
|
/// <param name="areaName">The area name.</param>
|
||||||
/// <param name="isPartial">Determines if the view being discovered is a partial.</param>
|
/// <param name="isPartial">Determines if the view being discovered is a partial.</param>
|
||||||
public ViewLocationExpanderContext(
|
public ViewLocationExpanderContext(
|
||||||
ActionContext actionContext,
|
ActionContext actionContext,
|
||||||
string viewName,
|
string viewName,
|
||||||
|
string controllerName,
|
||||||
|
string areaName,
|
||||||
bool isPartial)
|
bool isPartial)
|
||||||
{
|
{
|
||||||
if (actionContext == null)
|
if (actionContext == null)
|
||||||
|
|
@ -34,6 +38,8 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
ActionContext = actionContext;
|
ActionContext = actionContext;
|
||||||
ViewName = viewName;
|
ViewName = viewName;
|
||||||
|
ControllerName = controllerName;
|
||||||
|
AreaName = areaName;
|
||||||
IsPartial = isPartial;
|
IsPartial = isPartial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,6 +53,16 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ViewName { get; }
|
public string ViewName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the controller name.
|
||||||
|
/// </summary>
|
||||||
|
public string ControllerName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the area name.
|
||||||
|
/// </summary>
|
||||||
|
public string AreaName { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value that determines if a partial view is being discovered.
|
/// Gets a value that determines if a partial view is being discovered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1,41 +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.Linq;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public class ViewStartProvider : IViewStartProvider
|
|
||||||
{
|
|
||||||
private readonly IRazorPageFactory _pageFactory;
|
|
||||||
|
|
||||||
public ViewStartProvider(IRazorPageFactory pageFactory)
|
|
||||||
{
|
|
||||||
_pageFactory = pageFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IEnumerable<IRazorPage> GetViewStartPages(string path)
|
|
||||||
{
|
|
||||||
if (path == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
var viewStartLocations = ViewHierarchyUtility.GetViewStartLocations(path);
|
|
||||||
var viewStarts = viewStartLocations.Select(_pageFactory.CreateInstance)
|
|
||||||
.Where(p => p != null)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
// GetViewStartLocations return ViewStarts inside-out that is the _ViewStart closest to the page
|
|
||||||
// is the first: e.g. [ /Views/Home/_ViewStart, /Views/_ViewStart, /_ViewStart ]
|
|
||||||
// However they need to be executed outside in, so we'll reverse the sequence.
|
|
||||||
Array.Reverse(viewStarts);
|
|
||||||
|
|
||||||
return viewStarts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
// Arrange
|
// Arrange
|
||||||
var compilationFailure = new CompilationFailure("test", Enumerable.Empty<Microsoft.Extensions.PlatformAbstractions.DiagnosticMessage>());
|
var compilationFailure = new CompilationFailure("test", Enumerable.Empty<Microsoft.Extensions.PlatformAbstractions.DiagnosticMessage>());
|
||||||
var failures = new[] { compilationFailure };
|
var failures = new[] { compilationFailure };
|
||||||
var result = CompilationResult.Failed(failures);
|
var result = new CompilationResult(failures);
|
||||||
|
|
||||||
// Act and Assert
|
// Act and Assert
|
||||||
Assert.Null(result.CompiledType);
|
Assert.Null(result.CompiledType);
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
var result = cache.GetOrAdd("/some/path", ThrowsIfCalled);
|
var result = cache.GetOrAdd("/some/path", ThrowsIfCalled);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Same(CompilerCacheResult.FileNotFound, result);
|
Assert.False(result.Success);
|
||||||
Assert.Null(result.CompilationResult);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -48,18 +47,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
fileProvider.AddFile(ViewPath, "some content");
|
fileProvider.AddFile(ViewPath, "some content");
|
||||||
var cache = new CompilerCache(fileProvider);
|
var cache = new CompilerCache(fileProvider);
|
||||||
var type = typeof(TestView);
|
var type = typeof(TestView);
|
||||||
var expected = UncachedCompilationResult.Successful(type, "hello world");
|
var expected = new CompilationResult(type);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = cache.GetOrAdd(ViewPath, _ => expected);
|
var result = cache.GetOrAdd(ViewPath, _ => expected);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result);
|
Assert.True(result.Success);
|
||||||
var actual = Assert.IsType<UncachedCompilationResult>(result.CompilationResult);
|
Assert.Same(type, result.CompilationResult.CompiledType);
|
||||||
Assert.NotNull(actual);
|
|
||||||
Assert.Same(expected, actual);
|
|
||||||
Assert.Equal("hello world", actual.CompiledContent);
|
|
||||||
Assert.Same(type, actual.CompiledType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -75,15 +70,13 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
fileProvider.AddFile(viewPath, "some content");
|
fileProvider.AddFile(viewPath, "some content");
|
||||||
var cache = new CompilerCache(fileProvider);
|
var cache = new CompilerCache(fileProvider);
|
||||||
var type = typeof(TestView);
|
var type = typeof(TestView);
|
||||||
var expected = UncachedCompilationResult.Successful(type, "hello world");
|
var expected = new CompilationResult(type);
|
||||||
|
|
||||||
// Act - 1
|
// Act - 1
|
||||||
var result1 = cache.GetOrAdd(@"Areas\Finances\Views\Home\Index.cshtml", _ => expected);
|
var result1 = cache.GetOrAdd(@"Areas\Finances\Views\Home\Index.cshtml", _ => expected);
|
||||||
|
|
||||||
// Assert - 1
|
// Assert - 1
|
||||||
var compilationResult = Assert.IsType<UncachedCompilationResult>(result1.CompilationResult);
|
Assert.Same(type, result1.CompilationResult.CompiledType);
|
||||||
Assert.Same(expected, compilationResult);
|
|
||||||
Assert.Same(type, compilationResult.CompiledType);
|
|
||||||
|
|
||||||
// Act - 2
|
// Act - 2
|
||||||
var result2 = cache.GetOrAdd(relativePath, ThrowsIfCalled);
|
var result2 = cache.GetOrAdd(relativePath, ThrowsIfCalled);
|
||||||
|
|
@ -93,21 +86,21 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetOrAdd_ReturnsFileNotFoundIfFileWasDeleted()
|
public void GetOrAdd_ReturnsFailedCompilationResult_IfFileWasRemovedFromFileSystem()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var fileProvider = new TestFileProvider();
|
var fileProvider = new TestFileProvider();
|
||||||
fileProvider.AddFile(ViewPath, "some content");
|
fileProvider.AddFile(ViewPath, "some content");
|
||||||
var cache = new CompilerCache(fileProvider);
|
var cache = new CompilerCache(fileProvider);
|
||||||
var type = typeof(TestView);
|
var type = typeof(TestView);
|
||||||
var expected = UncachedCompilationResult.Successful(type, "hello world");
|
var expected = new CompilationResult(type);
|
||||||
|
|
||||||
// Act 1
|
// Act 1
|
||||||
var result1 = cache.GetOrAdd(ViewPath, _ => expected);
|
var result1 = cache.GetOrAdd(ViewPath, _ => expected);
|
||||||
|
|
||||||
// Assert 1
|
// Assert 1
|
||||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result1);
|
Assert.True(result1.Success);
|
||||||
Assert.Same(expected, result1.CompilationResult);
|
Assert.Same(expected.CompiledType, result1.CompilationResult.CompiledType);
|
||||||
|
|
||||||
// Act 2
|
// Act 2
|
||||||
// Delete the file from the file system and set it's expiration token.
|
// Delete the file from the file system and set it's expiration token.
|
||||||
|
|
@ -116,8 +109,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
|
var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
|
||||||
|
|
||||||
// Assert 2
|
// Assert 2
|
||||||
Assert.Same(CompilerCacheResult.FileNotFound, result2);
|
Assert.False(result2.Success);
|
||||||
Assert.Null(result2.CompilationResult);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -127,22 +119,22 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
var fileProvider = new TestFileProvider();
|
var fileProvider = new TestFileProvider();
|
||||||
fileProvider.AddFile(ViewPath, "some content");
|
fileProvider.AddFile(ViewPath, "some content");
|
||||||
var cache = new CompilerCache(fileProvider);
|
var cache = new CompilerCache(fileProvider);
|
||||||
var expected1 = UncachedCompilationResult.Successful(typeof(TestView), "hello world");
|
var expected1 = new CompilationResult(typeof(TestView));
|
||||||
var expected2 = UncachedCompilationResult.Successful(typeof(DifferentView), "different content");
|
var expected2 = new CompilationResult(typeof(DifferentView));
|
||||||
|
|
||||||
// Act 1
|
// Act 1
|
||||||
var result1 = cache.GetOrAdd(ViewPath, _ => expected1);
|
var result1 = cache.GetOrAdd(ViewPath, _ => expected1);
|
||||||
|
|
||||||
// Assert 1
|
// Assert 1
|
||||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result1);
|
Assert.True(result1.Success);
|
||||||
Assert.Same(expected1, result1.CompilationResult);
|
Assert.Same(typeof(TestView), result1.CompilationResult.CompiledType);
|
||||||
|
|
||||||
// Act 2
|
// Act 2
|
||||||
// Verify we're getting cached results.
|
// Verify we're getting cached results.
|
||||||
var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
|
var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
|
||||||
|
|
||||||
// Assert 2
|
// Assert 2
|
||||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result2);
|
Assert.True(result2.Success);
|
||||||
Assert.Same(expected1.CompiledType, result2.CompilationResult.CompiledType);
|
Assert.Same(expected1.CompiledType, result2.CompilationResult.CompiledType);
|
||||||
|
|
||||||
// Act 3
|
// Act 3
|
||||||
|
|
@ -150,8 +142,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
var result3 = cache.GetOrAdd(ViewPath, _ => expected2);
|
var result3 = cache.GetOrAdd(ViewPath, _ => expected2);
|
||||||
|
|
||||||
// Assert 3
|
// Assert 3
|
||||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result3);
|
Assert.True(result3.Success);
|
||||||
Assert.Same(expected2, result3.CompilationResult);
|
Assert.Same(expected2.CompiledType, result3.CompilationResult.CompiledType);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -162,22 +154,22 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
var fileProvider = new TestFileProvider();
|
var fileProvider = new TestFileProvider();
|
||||||
fileProvider.AddFile(ViewPath, "some content");
|
fileProvider.AddFile(ViewPath, "some content");
|
||||||
var cache = new CompilerCache(fileProvider);
|
var cache = new CompilerCache(fileProvider);
|
||||||
var expected1 = UncachedCompilationResult.Successful(typeof(TestView), "hello world");
|
var expected1 = new CompilationResult(typeof(TestView));
|
||||||
var expected2 = UncachedCompilationResult.Successful(typeof(DifferentView), "different content");
|
var expected2 = new CompilationResult(typeof(DifferentView));
|
||||||
|
|
||||||
// Act 1
|
// Act 1
|
||||||
var result1 = cache.GetOrAdd(ViewPath, _ => expected1);
|
var result1 = cache.GetOrAdd(ViewPath, _ => expected1);
|
||||||
|
|
||||||
// Assert 1
|
// Assert 1
|
||||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result1);
|
Assert.True(result1.Success);
|
||||||
Assert.Same(expected1, result1.CompilationResult);
|
Assert.Same(expected1.CompiledType, result1.CompilationResult.CompiledType);
|
||||||
|
|
||||||
// Act 2
|
// Act 2
|
||||||
// Verify we're getting cached results.
|
// Verify we're getting cached results.
|
||||||
var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
|
var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
|
||||||
|
|
||||||
// Assert 2
|
// Assert 2
|
||||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result2);
|
Assert.True(result2.Success);
|
||||||
Assert.Same(expected1.CompiledType, result2.CompilationResult.CompiledType);
|
Assert.Same(expected1.CompiledType, result2.CompilationResult.CompiledType);
|
||||||
|
|
||||||
// Act 3
|
// Act 3
|
||||||
|
|
@ -185,8 +177,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
var result3 = cache.GetOrAdd(ViewPath, _ => expected2);
|
var result3 = cache.GetOrAdd(ViewPath, _ => expected2);
|
||||||
|
|
||||||
// Assert 2
|
// Assert 2
|
||||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result3);
|
Assert.True(result3.Success);
|
||||||
Assert.Same(expected2, result3.CompilationResult);
|
Assert.Same(expected2.CompiledType, result3.CompilationResult.CompiledType);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -198,21 +190,20 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
fileProvider.AddFile(ViewPath, "some content");
|
fileProvider.AddFile(ViewPath, "some content");
|
||||||
var cache = new CompilerCache(fileProvider);
|
var cache = new CompilerCache(fileProvider);
|
||||||
var type = typeof(TestView);
|
var type = typeof(TestView);
|
||||||
var expected = UncachedCompilationResult.Successful(type, "hello world");
|
var expected = new CompilationResult(type);
|
||||||
|
|
||||||
// Act 1
|
// Act 1
|
||||||
var result1 = cache.GetOrAdd(ViewPath, _ => expected);
|
var result1 = cache.GetOrAdd(ViewPath, _ => expected);
|
||||||
|
|
||||||
// Assert 1
|
// Assert 1
|
||||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result1);
|
Assert.True(result1.Success);
|
||||||
Assert.Same(expected, result1.CompilationResult);
|
Assert.Same(type, result1.CompilationResult.CompiledType);
|
||||||
|
|
||||||
// Act 2
|
// Act 2
|
||||||
var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
|
var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
|
||||||
|
|
||||||
// Assert 2
|
// Assert 2
|
||||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result2);
|
Assert.True(result2.Success);
|
||||||
Assert.IsType<CompilationResult>(result2.CompilationResult);
|
|
||||||
Assert.Same(type, result2.CompilationResult.CompiledType);
|
Assert.Same(type, result2.CompilationResult.CompiledType);
|
||||||
mockFileProvider.Verify(v => v.GetFileInfo(ViewPath), Times.Once());
|
mockFileProvider.Verify(v => v.GetFileInfo(ViewPath), Times.Once());
|
||||||
}
|
}
|
||||||
|
|
@ -228,7 +219,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
var result = cache.GetOrAdd(PrecompiledViewsPath, ThrowsIfCalled);
|
var result = cache.GetOrAdd(PrecompiledViewsPath, ThrowsIfCalled);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result);
|
Assert.True(result.Success);
|
||||||
Assert.Same(typeof(PreCompile), result.CompilationResult.CompiledType);
|
Assert.Same(typeof(PreCompile), result.CompilationResult.CompiledType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -245,7 +236,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
var result = cache.GetOrAdd(PrecompiledViewsPath, ThrowsIfCalled);
|
var result = cache.GetOrAdd(PrecompiledViewsPath, ThrowsIfCalled);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result);
|
Assert.True(result.Success);
|
||||||
Assert.Same(typeof(PreCompile), result.CompilationResult.CompiledType);
|
Assert.Same(typeof(PreCompile), result.CompilationResult.CompiledType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,7 +254,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
var result = cache.GetOrAdd(PrecompiledViewsPath, ThrowsIfCalled);
|
var result = cache.GetOrAdd(PrecompiledViewsPath, ThrowsIfCalled);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result);
|
Assert.True(result.Success);
|
||||||
Assert.Same(typeof(PreCompile), result.CompilationResult.CompiledType);
|
Assert.Same(typeof(PreCompile), result.CompilationResult.CompiledType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -274,26 +265,26 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
var fileProvider = new TestFileProvider();
|
var fileProvider = new TestFileProvider();
|
||||||
fileProvider.AddFile(ViewPath, "some content");
|
fileProvider.AddFile(ViewPath, "some content");
|
||||||
var cache = new CompilerCache(fileProvider, _precompiledViews);
|
var cache = new CompilerCache(fileProvider, _precompiledViews);
|
||||||
var expected = CompilationResult.Successful(typeof(TestView));
|
var expected = new CompilationResult(typeof(TestView));
|
||||||
|
|
||||||
// Act 1
|
// Act 1
|
||||||
var result1 = cache.GetOrAdd(ViewPath, _ => expected);
|
var result1 = cache.GetOrAdd(ViewPath, _ => expected);
|
||||||
|
|
||||||
// Assert 1
|
// Assert 1
|
||||||
Assert.Same(expected, result1.CompilationResult);
|
Assert.Same(typeof(TestView), result1.CompilationResult.CompiledType);
|
||||||
|
|
||||||
// Act 2
|
// Act 2
|
||||||
var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
|
var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
|
||||||
|
|
||||||
// Assert 2
|
// Assert 2
|
||||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result2);
|
Assert.True(result2.Success);
|
||||||
Assert.Same(typeof(TestView), result2.CompilationResult.CompiledType);
|
Assert.Same(typeof(TestView), result2.CompilationResult.CompiledType);
|
||||||
|
|
||||||
// Act 3
|
// Act 3
|
||||||
var result3 = cache.GetOrAdd(PrecompiledViewsPath, ThrowsIfCalled);
|
var result3 = cache.GetOrAdd(PrecompiledViewsPath, ThrowsIfCalled);
|
||||||
|
|
||||||
// Assert 3
|
// Assert 3
|
||||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result2);
|
Assert.True(result2.Success);
|
||||||
Assert.Same(typeof(PreCompile), result3.CompilationResult.CompiledType);
|
Assert.Same(typeof(PreCompile), result3.CompilationResult.CompiledType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
|
|
||||||
var compiler = new Mock<ICompilationService>();
|
var compiler = new Mock<ICompilationService>();
|
||||||
compiler.Setup(c => c.Compile(relativeFileInfo, It.IsAny<string>()))
|
compiler.Setup(c => c.Compile(relativeFileInfo, It.IsAny<string>()))
|
||||||
.Returns(CompilationResult.Successful(typeof(RazorCompilationServiceTest)));
|
.Returns(new CompilationResult(typeof(RazorCompilationServiceTest)));
|
||||||
|
|
||||||
var razorService = new RazorCompilationService(compiler.Object, host.Object, GetOptions());
|
var razorService = new RazorCompilationService(compiler.Object, host.Object, GetOptions());
|
||||||
|
|
||||||
|
|
@ -106,7 +106,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
.Returns(Stream.Null);
|
.Returns(Stream.Null);
|
||||||
var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, @"Views\index\home.cshtml");
|
var relativeFileInfo = new RelativeFileInfo(fileInfo.Object, @"Views\index\home.cshtml");
|
||||||
|
|
||||||
var compilationResult = CompilationResult.Successful(typeof(object));
|
var compilationResult = new CompilationResult(typeof(object));
|
||||||
var compiler = new Mock<ICompilationService>();
|
var compiler = new Mock<ICompilationService>();
|
||||||
compiler.Setup(c => c.Compile(relativeFileInfo, code))
|
compiler.Setup(c => c.Compile(relativeFileInfo, code))
|
||||||
.Returns(compilationResult)
|
.Returns(compilationResult)
|
||||||
|
|
@ -117,7 +117,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
var result = razorService.Compile(relativeFileInfo);
|
var result = razorService.Compile(relativeFileInfo);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Same(compilationResult, result);
|
Assert.Same(compilationResult.CompiledType, result.CompiledType);
|
||||||
compiler.Verify();
|
compiler.Verify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||||
public class RoslynCompilationServiceTest
|
public class RoslynCompilationServiceTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Compile_ReturnsUncachedCompilationResultWithCompiledContent()
|
public void Compile_ReturnsCompilationResult()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var content = @"
|
var content = @"
|
||||||
|
|
@ -29,29 +29,31 @@ public class MyTestType {}";
|
||||||
var libraryExporter = GetLibraryExporter();
|
var libraryExporter = GetLibraryExporter();
|
||||||
|
|
||||||
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
||||||
compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName,
|
compilerOptionsProvider
|
||||||
applicationEnvironment.RuntimeFramework,
|
.Setup(p => p.GetCompilerOptions(
|
||||||
applicationEnvironment.Configuration))
|
applicationEnvironment.ApplicationName,
|
||||||
.Returns(new CompilerOptions());
|
applicationEnvironment.RuntimeFramework,
|
||||||
|
applicationEnvironment.Configuration))
|
||||||
|
.Returns(new CompilerOptions());
|
||||||
var mvcRazorHost = new Mock<IMvcRazorHost>();
|
var mvcRazorHost = new Mock<IMvcRazorHost>();
|
||||||
mvcRazorHost.SetupGet(m => m.MainClassNamePrefix)
|
mvcRazorHost.SetupGet(m => m.MainClassNamePrefix)
|
||||||
.Returns(string.Empty);
|
.Returns(string.Empty);
|
||||||
|
|
||||||
var compilationService = new RoslynCompilationService(applicationEnvironment,
|
var compilationService = new RoslynCompilationService(
|
||||||
libraryExporter,
|
applicationEnvironment,
|
||||||
compilerOptionsProvider.Object,
|
libraryExporter,
|
||||||
mvcRazorHost.Object,
|
compilerOptionsProvider.Object,
|
||||||
GetOptions());
|
mvcRazorHost.Object,
|
||||||
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
|
GetOptions());
|
||||||
|
var relativeFileInfo = new RelativeFileInfo(
|
||||||
|
new TestFileInfo { PhysicalPath = "SomePath" },
|
||||||
"some-relative-path");
|
"some-relative-path");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = compilationService.Compile(relativeFileInfo, content);
|
var result = compilationService.Compile(relativeFileInfo, content);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var uncachedResult = Assert.IsType<UncachedCompilationResult>(result);
|
|
||||||
Assert.Equal("MyTestType", result.CompiledType.Name);
|
Assert.Equal("MyTestType", result.CompiledType.Name);
|
||||||
Assert.Equal(content, uncachedResult.CompiledContent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -67,19 +69,22 @@ this should fail";
|
||||||
var libraryExporter = GetLibraryExporter();
|
var libraryExporter = GetLibraryExporter();
|
||||||
|
|
||||||
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
||||||
compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName,
|
compilerOptionsProvider
|
||||||
applicationEnvironment.RuntimeFramework,
|
.Setup(p => p.GetCompilerOptions(
|
||||||
applicationEnvironment.Configuration))
|
applicationEnvironment.ApplicationName,
|
||||||
.Returns(new CompilerOptions());
|
applicationEnvironment.RuntimeFramework,
|
||||||
|
applicationEnvironment.Configuration))
|
||||||
|
.Returns(new CompilerOptions());
|
||||||
var mvcRazorHost = Mock.Of<IMvcRazorHost>();
|
var mvcRazorHost = Mock.Of<IMvcRazorHost>();
|
||||||
var fileProvider = new TestFileProvider();
|
var fileProvider = new TestFileProvider();
|
||||||
var fileInfo = fileProvider.AddFile(viewPath, fileContent);
|
var fileInfo = fileProvider.AddFile(viewPath, fileContent);
|
||||||
|
|
||||||
var compilationService = new RoslynCompilationService(applicationEnvironment,
|
var compilationService = new RoslynCompilationService(
|
||||||
libraryExporter,
|
applicationEnvironment,
|
||||||
compilerOptionsProvider.Object,
|
libraryExporter,
|
||||||
mvcRazorHost,
|
compilerOptionsProvider.Object,
|
||||||
GetOptions(fileProvider));
|
mvcRazorHost,
|
||||||
|
GetOptions(fileProvider));
|
||||||
var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path");
|
var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -103,18 +108,22 @@ this should fail";
|
||||||
var libraryExporter = GetLibraryExporter();
|
var libraryExporter = GetLibraryExporter();
|
||||||
|
|
||||||
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
||||||
compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName,
|
compilerOptionsProvider
|
||||||
applicationEnvironment.RuntimeFramework,
|
.Setup(p => p.GetCompilerOptions(
|
||||||
applicationEnvironment.Configuration))
|
applicationEnvironment.ApplicationName,
|
||||||
.Returns(new CompilerOptions());
|
applicationEnvironment.RuntimeFramework,
|
||||||
|
applicationEnvironment.Configuration))
|
||||||
|
.Returns(new CompilerOptions());
|
||||||
var mvcRazorHost = Mock.Of<IMvcRazorHost>();
|
var mvcRazorHost = Mock.Of<IMvcRazorHost>();
|
||||||
|
|
||||||
var compilationService = new RoslynCompilationService(applicationEnvironment,
|
var compilationService = new RoslynCompilationService(
|
||||||
libraryExporter,
|
applicationEnvironment,
|
||||||
compilerOptionsProvider.Object,
|
libraryExporter,
|
||||||
mvcRazorHost,
|
compilerOptionsProvider.Object,
|
||||||
GetOptions());
|
mvcRazorHost,
|
||||||
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { Content = fileContent },
|
GetOptions());
|
||||||
|
var relativeFileInfo = new RelativeFileInfo(
|
||||||
|
new TestFileInfo { Content = fileContent },
|
||||||
"some-relative-path");
|
"some-relative-path");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -141,10 +150,12 @@ this should fail";
|
||||||
var libraryExporter = GetLibraryExporter();
|
var libraryExporter = GetLibraryExporter();
|
||||||
|
|
||||||
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
||||||
compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName,
|
compilerOptionsProvider
|
||||||
applicationEnvironment.RuntimeFramework,
|
.Setup(p => p.GetCompilerOptions(
|
||||||
applicationEnvironment.Configuration))
|
applicationEnvironment.ApplicationName,
|
||||||
.Returns(new CompilerOptions());
|
applicationEnvironment.RuntimeFramework,
|
||||||
|
applicationEnvironment.Configuration))
|
||||||
|
.Returns(new CompilerOptions());
|
||||||
var mvcRazorHost = Mock.Of<IMvcRazorHost>();
|
var mvcRazorHost = Mock.Of<IMvcRazorHost>();
|
||||||
|
|
||||||
var mockFileInfo = new Mock<IFileInfo>();
|
var mockFileInfo = new Mock<IFileInfo>();
|
||||||
|
|
@ -153,11 +164,12 @@ this should fail";
|
||||||
var fileProvider = new TestFileProvider();
|
var fileProvider = new TestFileProvider();
|
||||||
fileProvider.AddFile(path, mockFileInfo.Object);
|
fileProvider.AddFile(path, mockFileInfo.Object);
|
||||||
|
|
||||||
var compilationService = new RoslynCompilationService(applicationEnvironment,
|
var compilationService = new RoslynCompilationService(
|
||||||
libraryExporter,
|
applicationEnvironment,
|
||||||
compilerOptionsProvider.Object,
|
libraryExporter,
|
||||||
mvcRazorHost,
|
compilerOptionsProvider.Object,
|
||||||
GetOptions(fileProvider));
|
mvcRazorHost,
|
||||||
|
GetOptions(fileProvider));
|
||||||
|
|
||||||
var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, path);
|
var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, path);
|
||||||
|
|
||||||
|
|
@ -187,20 +199,24 @@ public class MyNonCustomDefinedClass {}
|
||||||
var libraryExporter = GetLibraryExporter();
|
var libraryExporter = GetLibraryExporter();
|
||||||
|
|
||||||
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
||||||
compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName,
|
compilerOptionsProvider
|
||||||
applicationEnvironment.RuntimeFramework,
|
.Setup(p => p.GetCompilerOptions(
|
||||||
applicationEnvironment.Configuration))
|
applicationEnvironment.ApplicationName,
|
||||||
.Returns(new CompilerOptions { Defines = new[] { "MY_CUSTOM_DEFINE" } });
|
applicationEnvironment.RuntimeFramework,
|
||||||
|
applicationEnvironment.Configuration))
|
||||||
|
.Returns(new CompilerOptions { Defines = new[] { "MY_CUSTOM_DEFINE" } });
|
||||||
var mvcRazorHost = new Mock<IMvcRazorHost>();
|
var mvcRazorHost = new Mock<IMvcRazorHost>();
|
||||||
mvcRazorHost.SetupGet(m => m.MainClassNamePrefix)
|
mvcRazorHost.SetupGet(m => m.MainClassNamePrefix)
|
||||||
.Returns("My");
|
.Returns("My");
|
||||||
|
|
||||||
var compilationService = new RoslynCompilationService(applicationEnvironment,
|
var compilationService = new RoslynCompilationService(
|
||||||
libraryExporter,
|
applicationEnvironment,
|
||||||
compilerOptionsProvider.Object,
|
libraryExporter,
|
||||||
mvcRazorHost.Object,
|
compilerOptionsProvider.Object,
|
||||||
GetOptions());
|
mvcRazorHost.Object,
|
||||||
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
|
GetOptions());
|
||||||
|
var relativeFileInfo = new RelativeFileInfo(
|
||||||
|
new TestFileInfo { PhysicalPath = "SomePath" },
|
||||||
"some-relative-path");
|
"some-relative-path");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -222,21 +238,25 @@ public class NotRazorPrefixType {}";
|
||||||
var libraryExporter = GetLibraryExporter();
|
var libraryExporter = GetLibraryExporter();
|
||||||
|
|
||||||
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
||||||
compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName,
|
compilerOptionsProvider
|
||||||
applicationEnvironment.RuntimeFramework,
|
.Setup(p => p.GetCompilerOptions(
|
||||||
applicationEnvironment.Configuration))
|
applicationEnvironment.ApplicationName,
|
||||||
.Returns(new CompilerOptions());
|
applicationEnvironment.RuntimeFramework,
|
||||||
|
applicationEnvironment.Configuration))
|
||||||
|
.Returns(new CompilerOptions());
|
||||||
var mvcRazorHost = new Mock<IMvcRazorHost>();
|
var mvcRazorHost = new Mock<IMvcRazorHost>();
|
||||||
mvcRazorHost.SetupGet(m => m.MainClassNamePrefix)
|
mvcRazorHost.SetupGet(m => m.MainClassNamePrefix)
|
||||||
.Returns("RazorPrefix");
|
.Returns("RazorPrefix");
|
||||||
|
|
||||||
var compilationService = new RoslynCompilationService(applicationEnvironment,
|
var compilationService = new RoslynCompilationService(
|
||||||
libraryExporter,
|
applicationEnvironment,
|
||||||
compilerOptionsProvider.Object,
|
libraryExporter,
|
||||||
mvcRazorHost.Object,
|
compilerOptionsProvider.Object,
|
||||||
GetOptions());
|
mvcRazorHost.Object,
|
||||||
|
GetOptions());
|
||||||
|
|
||||||
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
|
var relativeFileInfo = new RelativeFileInfo(
|
||||||
|
new TestFileInfo { PhysicalPath = "SomePath" },
|
||||||
"some-relative-path");
|
"some-relative-path");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -356,27 +376,33 @@ public class NotRazorPrefixType {}";
|
||||||
private static ILibraryExporter GetLibraryExporter()
|
private static ILibraryExporter GetLibraryExporter()
|
||||||
{
|
{
|
||||||
var fileReference = new Mock<IMetadataFileReference>();
|
var fileReference = new Mock<IMetadataFileReference>();
|
||||||
fileReference.SetupGet(f => f.Path)
|
fileReference
|
||||||
.Returns(typeof(string).Assembly.Location);
|
.SetupGet(f => f.Path)
|
||||||
|
.Returns(typeof(string).Assembly.Location);
|
||||||
var libraryExport = new LibraryExport(fileReference.Object);
|
var libraryExport = new LibraryExport(fileReference.Object);
|
||||||
|
|
||||||
var libraryExporter = new Mock<ILibraryExporter>();
|
var libraryExporter = new Mock<ILibraryExporter>();
|
||||||
libraryExporter.Setup(l => l.GetAllExports(It.IsAny<string>()))
|
libraryExporter
|
||||||
.Returns(libraryExport);
|
.Setup(l => l.GetAllExports(It.IsAny<string>()))
|
||||||
|
.Returns(libraryExport);
|
||||||
return libraryExporter.Object;
|
return libraryExporter.Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IApplicationEnvironment GetApplicationEnvironment()
|
private IApplicationEnvironment GetApplicationEnvironment()
|
||||||
{
|
{
|
||||||
var applicationEnvironment = new Mock<IApplicationEnvironment>();
|
var applicationEnvironment = new Mock<IApplicationEnvironment>();
|
||||||
applicationEnvironment.SetupGet(a => a.ApplicationName)
|
applicationEnvironment
|
||||||
.Returns("MyApp");
|
.SetupGet(a => a.ApplicationName)
|
||||||
applicationEnvironment.SetupGet(a => a.RuntimeFramework)
|
.Returns("MyApp");
|
||||||
.Returns(new FrameworkName("ASPNET", new Version(5, 0)));
|
applicationEnvironment
|
||||||
applicationEnvironment.SetupGet(a => a.Configuration)
|
.SetupGet(a => a.RuntimeFramework)
|
||||||
.Returns("Debug");
|
.Returns(new FrameworkName("ASPNET", new Version(5, 0)));
|
||||||
applicationEnvironment.SetupGet(a => a.ApplicationBasePath)
|
applicationEnvironment
|
||||||
.Returns("MyBasePath");
|
.SetupGet(a => a.Configuration)
|
||||||
|
.Returns("Debug");
|
||||||
|
applicationEnvironment
|
||||||
|
.SetupGet(a => a.ApplicationBasePath)
|
||||||
|
.Returns("MyBasePath");
|
||||||
|
|
||||||
return applicationEnvironment.Object;
|
return applicationEnvironment.Object;
|
||||||
}
|
}
|
||||||
|
|
@ -388,7 +414,8 @@ public class NotRazorPrefixType {}";
|
||||||
FileProvider = fileProvider ?? new TestFileProvider()
|
FileProvider = fileProvider ?? new TestFileProvider()
|
||||||
};
|
};
|
||||||
var options = new Mock<IOptions<RazorViewEngineOptions>>();
|
var options = new Mock<IOptions<RazorViewEngineOptions>>();
|
||||||
options.SetupGet(o => o.Value)
|
options
|
||||||
|
.SetupGet(o => o.Value)
|
||||||
.Returns(razorViewEngineOptions);
|
.Returns(razorViewEngineOptions);
|
||||||
|
|
||||||
return options.Object;
|
return options.Object;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
// 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.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Mvc.Razor.Compilation;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
|
{
|
||||||
|
public class DefaultRazorPageFactoryProviderTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void CreateFactory_ReturnsExpirationTokensFromCompilerCache_ForUnsuccessfulResults()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expirationTokens = new[]
|
||||||
|
{
|
||||||
|
Mock.Of<IChangeToken>(),
|
||||||
|
Mock.Of<IChangeToken>(),
|
||||||
|
};
|
||||||
|
var compilerCache = new Mock<ICompilerCache>();
|
||||||
|
compilerCache
|
||||||
|
.Setup(f => f.GetOrAdd(It.IsAny<string>(), It.IsAny<Func<RelativeFileInfo, CompilationResult>>()))
|
||||||
|
.Returns(new CompilerCacheResult(expirationTokens));
|
||||||
|
var compilerCacheProvider = new Mock<ICompilerCacheProvider>();
|
||||||
|
compilerCacheProvider
|
||||||
|
.SetupGet(c => c.Cache)
|
||||||
|
.Returns(compilerCache.Object);
|
||||||
|
var factoryProvider = new DefaultRazorPageFactoryProvider(
|
||||||
|
Mock.Of<IRazorCompilationService>(),
|
||||||
|
compilerCacheProvider.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = factoryProvider.CreateFactory("/file-does-not-exist");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result.Success);
|
||||||
|
Assert.Equal(expirationTokens, result.ExpirationTokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateFactory_ReturnsExpirationTokensFromCompilerCache_ForSuccessfulResults()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expirationTokens = new[]
|
||||||
|
{
|
||||||
|
Mock.Of<IChangeToken>(),
|
||||||
|
Mock.Of<IChangeToken>(),
|
||||||
|
};
|
||||||
|
var compilerCache = new Mock<ICompilerCache>();
|
||||||
|
compilerCache
|
||||||
|
.Setup(f => f.GetOrAdd(It.IsAny<string>(), It.IsAny<Func<RelativeFileInfo, CompilationResult>>()))
|
||||||
|
.Returns(new CompilerCacheResult(new CompilationResult(typeof(object)), expirationTokens));
|
||||||
|
var compilerCacheProvider = new Mock<ICompilerCacheProvider>();
|
||||||
|
compilerCacheProvider
|
||||||
|
.SetupGet(c => c.Cache)
|
||||||
|
.Returns(compilerCache.Object);
|
||||||
|
var factoryProvider = new DefaultRazorPageFactoryProvider(
|
||||||
|
Mock.Of<IRazorCompilationService>(),
|
||||||
|
compilerCacheProvider.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = factoryProvider.CreateFactory("/file-exists");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result.Success);
|
||||||
|
Assert.Equal(expirationTokens, result.ExpirationTokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateFactory_ProducesDelegateThatSetsPagePath()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var compilerCache = new Mock<ICompilerCache>();
|
||||||
|
compilerCache
|
||||||
|
.Setup(f => f.GetOrAdd(It.IsAny<string>(), It.IsAny<Func<RelativeFileInfo, CompilationResult>>()))
|
||||||
|
.Returns(new CompilerCacheResult(new CompilationResult(typeof(TestRazorPage)), new IChangeToken[0]));
|
||||||
|
var compilerCacheProvider = new Mock<ICompilerCacheProvider>();
|
||||||
|
compilerCacheProvider
|
||||||
|
.SetupGet(c => c.Cache)
|
||||||
|
.Returns(compilerCache.Object);
|
||||||
|
var factoryProvider = new DefaultRazorPageFactoryProvider(
|
||||||
|
Mock.Of<IRazorCompilationService>(),
|
||||||
|
compilerCacheProvider.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = factoryProvider.CreateFactory("/file-exists");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result.Success);
|
||||||
|
var actual = result.RazorPageFactory();
|
||||||
|
Assert.Equal("/file-exists", actual.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestRazorPage : RazorPage
|
||||||
|
{
|
||||||
|
public override Task ExecuteAsync()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,522 +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 Microsoft.AspNet.Http.Internal;
|
|
||||||
using Microsoft.AspNet.Mvc.Abstractions;
|
|
||||||
using Microsoft.AspNet.Mvc.Routing;
|
|
||||||
using Microsoft.AspNet.Routing;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
|
||||||
{
|
|
||||||
public class DefaultViewLocationCacheTest
|
|
||||||
{
|
|
||||||
public static IEnumerable<object[]> CacheEntryData
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
yield return new[] { new ViewLocationExpanderContext(GetActionContext(), "test", isPartial: false) };
|
|
||||||
yield return new[] { new ViewLocationExpanderContext(GetActionContext(), "test", isPartial: true) };
|
|
||||||
|
|
||||||
var areaActionContext = GetActionContext("controller2", "myarea");
|
|
||||||
yield return new[] { new ViewLocationExpanderContext(areaActionContext, "test2", isPartial: false) };
|
|
||||||
yield return new[] { new ViewLocationExpanderContext(areaActionContext, "test2", isPartial: true) };
|
|
||||||
|
|
||||||
var actionContext = GetActionContext("controller3", "area3");
|
|
||||||
var values = new Dictionary<string, string>(StringComparer.Ordinal)
|
|
||||||
{
|
|
||||||
{ "culture", "fr" },
|
|
||||||
{ "theme", "sleek" }
|
|
||||||
};
|
|
||||||
var expanderContext = new ViewLocationExpanderContext(actionContext, "test3", isPartial: false)
|
|
||||||
{
|
|
||||||
Values = values
|
|
||||||
};
|
|
||||||
yield return new[] { expanderContext };
|
|
||||||
|
|
||||||
expanderContext = new ViewLocationExpanderContext(actionContext, "test3", isPartial: true)
|
|
||||||
{
|
|
||||||
Values = values
|
|
||||||
};
|
|
||||||
yield return new[] { expanderContext };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DefaultViewLocationCache.ViewLocationCacheKeyComparer CacheKeyComparer =>
|
|
||||||
DefaultViewLocationCache.ViewLocationCacheKeyComparer.Instance;
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[MemberData(nameof(CacheEntryData))]
|
|
||||||
public void Get_ReturnsNoneResultIfItemDoesNotExist(ViewLocationExpanderContext context)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var cache = new DefaultViewLocationCache();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var result = cache.Get(context);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(result, ViewLocationCacheResult.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[MemberData(nameof(CacheEntryData))]
|
|
||||||
public void InvokingGetAfterSet_ReturnsCachedItem(ViewLocationExpanderContext context)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var cache = new DefaultViewLocationCache();
|
|
||||||
var value = new ViewLocationCacheResult(
|
|
||||||
Guid.NewGuid().ToString(),
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
Guid.NewGuid().ToString(),
|
|
||||||
Guid.NewGuid().ToString()
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act - 1
|
|
||||||
cache.Set(context, value);
|
|
||||||
var result = cache.Get(context);
|
|
||||||
|
|
||||||
// Assert - 1
|
|
||||||
Assert.Equal(value, result);
|
|
||||||
|
|
||||||
// Act - 2
|
|
||||||
result = cache.Get(context);
|
|
||||||
|
|
||||||
// Assert - 2
|
|
||||||
Assert.Equal(value, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData("View1", "View2")]
|
|
||||||
[InlineData("View1", "view1")]
|
|
||||||
public void ViewLocationCacheKeyComparer_EqualsReturnsFalseIfViewNamesAreDifferent(
|
|
||||||
string viewName1,
|
|
||||||
string viewName2)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var actionContext = GetActionContext();
|
|
||||||
var viewLocationExpanderContext1 = new ViewLocationExpanderContext(
|
|
||||||
actionContext,
|
|
||||||
viewName1,
|
|
||||||
isPartial: true);
|
|
||||||
var viewLocationExpanderContext2 = new ViewLocationExpanderContext(
|
|
||||||
actionContext,
|
|
||||||
viewName2,
|
|
||||||
isPartial: true);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var key1 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext1,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var key2 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext2,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var result = CacheKeyComparer.Equals(key1, key2);
|
|
||||||
var hash1 = CacheKeyComparer.GetHashCode(key1);
|
|
||||||
var hash2 = CacheKeyComparer.GetHashCode(key2);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.False(result);
|
|
||||||
Assert.NotEqual(hash1, hash2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(false, true)]
|
|
||||||
[InlineData(true, false)]
|
|
||||||
public void ViewLocationCacheKeyComparer_EqualsReturnsFalseIfIsPartialAreDifferent(
|
|
||||||
bool isPartial1,
|
|
||||||
bool isPartial2)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var actionContext = GetActionContext();
|
|
||||||
var viewLocationExpanderContext1 = new ViewLocationExpanderContext(
|
|
||||||
actionContext,
|
|
||||||
"View1",
|
|
||||||
isPartial1);
|
|
||||||
var viewLocationExpanderContext2 = new ViewLocationExpanderContext(
|
|
||||||
actionContext,
|
|
||||||
"View1",
|
|
||||||
isPartial2);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var key1 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext1,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var key2 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext2,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var result = CacheKeyComparer.Equals(key1, key2);
|
|
||||||
var hash1 = CacheKeyComparer.GetHashCode(key1);
|
|
||||||
var hash2 = CacheKeyComparer.GetHashCode(key2);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.False(result);
|
|
||||||
Assert.NotEqual(hash1, hash2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData("Controller1", "Controller2")]
|
|
||||||
[InlineData("controller1", "Controller1")]
|
|
||||||
public void ViewLocationCacheKeyComparer_EqualsReturnsFalseIfIsControllerNamesAreDifferent(
|
|
||||||
string controller1,
|
|
||||||
string controller2)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var viewLocationExpanderContext1 = new ViewLocationExpanderContext(
|
|
||||||
GetActionContext(controller1),
|
|
||||||
"View1",
|
|
||||||
isPartial: false);
|
|
||||||
var viewLocationExpanderContext2 = new ViewLocationExpanderContext(
|
|
||||||
GetActionContext(controller2),
|
|
||||||
"View1",
|
|
||||||
isPartial: false);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var key1 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext1,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var key2 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext2,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var result = CacheKeyComparer.Equals(key1, key2);
|
|
||||||
var hash1 = CacheKeyComparer.GetHashCode(key1);
|
|
||||||
var hash2 = CacheKeyComparer.GetHashCode(key2);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.False(result);
|
|
||||||
Assert.NotEqual(hash1, hash2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData("area1", null)]
|
|
||||||
[InlineData("Area1", "Area2")]
|
|
||||||
[InlineData("area1", "aRea1")]
|
|
||||||
public void ViewLocationCacheKeyComparer_EqualsReturnsFalseIfIsAreaNamesAreDifferent(
|
|
||||||
string area1,
|
|
||||||
string area2)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var viewLocationExpanderContext1 = new ViewLocationExpanderContext(
|
|
||||||
GetActionContext("Controller1", area1),
|
|
||||||
"View1",
|
|
||||||
isPartial: false);
|
|
||||||
var viewLocationExpanderContext2 = new ViewLocationExpanderContext(
|
|
||||||
GetActionContext("Controller1", area2),
|
|
||||||
"View1",
|
|
||||||
isPartial: false);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var key1 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext1,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var key2 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext2,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var result = CacheKeyComparer.Equals(key1, key2);
|
|
||||||
var hash1 = CacheKeyComparer.GetHashCode(key1);
|
|
||||||
var hash2 = CacheKeyComparer.GetHashCode(key2);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.False(result);
|
|
||||||
Assert.NotEqual(hash1, hash2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ViewLocationCacheKeyComparer_EqualsReturnsTrueIfControllerAreaAndViewNamesAreIdentical()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var viewLocationExpanderContext1 = new ViewLocationExpanderContext(
|
|
||||||
GetActionContext("Controller1", "Area1"),
|
|
||||||
"View1",
|
|
||||||
isPartial: false);
|
|
||||||
var viewLocationExpanderContext2 = new ViewLocationExpanderContext(
|
|
||||||
GetActionContext("Controller1", "Area1"),
|
|
||||||
"View1",
|
|
||||||
isPartial: false);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var key1 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext1,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var key2 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext2,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var result = CacheKeyComparer.Equals(key1, key2);
|
|
||||||
var hash1 = CacheKeyComparer.GetHashCode(key1);
|
|
||||||
var hash2 = CacheKeyComparer.GetHashCode(key2);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.True(result);
|
|
||||||
Assert.Equal(hash1, hash2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ViewLocationCacheKeyComparer_EqualsReturnsFalseIfViewLocationExpanderIsNullForEitherKey()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var viewLocationExpanderContext1 = new ViewLocationExpanderContext(
|
|
||||||
GetActionContext("Controller1", "Area1"),
|
|
||||||
"View1",
|
|
||||||
isPartial: false);
|
|
||||||
viewLocationExpanderContext1.Values = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "somekey", "somevalue" }
|
|
||||||
};
|
|
||||||
|
|
||||||
var viewLocationExpanderContext2 = new ViewLocationExpanderContext(
|
|
||||||
GetActionContext("Controller1", "Area1"),
|
|
||||||
"View1",
|
|
||||||
isPartial: false);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var key1 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext1,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var key2 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext2,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var result = CacheKeyComparer.Equals(key1, key2);
|
|
||||||
var hash1 = CacheKeyComparer.GetHashCode(key1);
|
|
||||||
var hash2 = CacheKeyComparer.GetHashCode(key2);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.False(result);
|
|
||||||
Assert.NotEqual(hash1, hash2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void ViewLocationCacheKeyComparer_EqualsReturnsFalseIfExpanderValueCountIsDifferent()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var viewLocationExpanderContext1 = new ViewLocationExpanderContext(
|
|
||||||
GetActionContext("Controller1", "Area1"),
|
|
||||||
"View1",
|
|
||||||
isPartial: false);
|
|
||||||
viewLocationExpanderContext1.Values = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "somekey", "somevalue" }
|
|
||||||
};
|
|
||||||
|
|
||||||
var viewLocationExpanderContext2 = new ViewLocationExpanderContext(
|
|
||||||
GetActionContext("Controller1", "Area1"),
|
|
||||||
"View1",
|
|
||||||
isPartial: false);
|
|
||||||
viewLocationExpanderContext2.Values = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "somekey", "somevalue" },
|
|
||||||
{ "somekey2", "somevalue2" },
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var key1 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext1,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var key2 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext2,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var result = CacheKeyComparer.Equals(key1, key2);
|
|
||||||
var hash1 = CacheKeyComparer.GetHashCode(key1);
|
|
||||||
var hash2 = CacheKeyComparer.GetHashCode(key2);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.False(result);
|
|
||||||
Assert.NotEqual(hash1, hash2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData("key1", "key2")]
|
|
||||||
[InlineData("Key1", "key1")]
|
|
||||||
public void ViewLocationCacheKeyComparer_EqualsReturnsFalseIfKeysAreDifferent(
|
|
||||||
string keyName1,
|
|
||||||
string keyName2)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var viewLocationExpanderContext1 = new ViewLocationExpanderContext(
|
|
||||||
GetActionContext("Controller1", "Area1"),
|
|
||||||
"View1",
|
|
||||||
isPartial: false);
|
|
||||||
viewLocationExpanderContext1.Values = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ keyName1, "somevalue" }
|
|
||||||
};
|
|
||||||
|
|
||||||
var viewLocationExpanderContext2 = new ViewLocationExpanderContext(
|
|
||||||
GetActionContext("Controller1", "Area1"),
|
|
||||||
"View1",
|
|
||||||
isPartial: false);
|
|
||||||
viewLocationExpanderContext2.Values = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ keyName2, "somevalue" },
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var key1 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext1,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var key2 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext2,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var result = CacheKeyComparer.Equals(key1, key2);
|
|
||||||
var hash1 = CacheKeyComparer.GetHashCode(key1);
|
|
||||||
var hash2 = CacheKeyComparer.GetHashCode(key2);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.False(result);
|
|
||||||
Assert.NotEqual(hash1, hash2);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData("value1", null)]
|
|
||||||
[InlineData("value1", "value2")]
|
|
||||||
[InlineData("value1", "Value1")]
|
|
||||||
public void ViewLocationCacheKeyComparer_EqualsReturnsFalseIfValuesAreDifferent(
|
|
||||||
string value1,
|
|
||||||
string value2)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var viewLocationExpanderContext1 = new ViewLocationExpanderContext(
|
|
||||||
GetActionContext("Controller1", "Area1"),
|
|
||||||
"View1",
|
|
||||||
isPartial: false);
|
|
||||||
viewLocationExpanderContext1.Values = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "somekey", value1 }
|
|
||||||
};
|
|
||||||
|
|
||||||
var viewLocationExpanderContext2 = new ViewLocationExpanderContext(
|
|
||||||
GetActionContext("Controller1", "Area1"),
|
|
||||||
"View1",
|
|
||||||
isPartial: false);
|
|
||||||
viewLocationExpanderContext2.Values = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "somekey", value2 },
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var key1 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext1,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var key2 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext2,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var result = CacheKeyComparer.Equals(key1, key2);
|
|
||||||
var hash1 = CacheKeyComparer.GetHashCode(key1);
|
|
||||||
var hash2 = CacheKeyComparer.GetHashCode(key2);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.False(result);
|
|
||||||
Assert.NotEqual(hash1, hash2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ViewLocationCacheKeyComparer_EqualsReturnsTrueIfValuesAreSame()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var viewLocationExpanderContext1 = new ViewLocationExpanderContext(
|
|
||||||
GetActionContext("Controller1", "Area1"),
|
|
||||||
"View1",
|
|
||||||
isPartial: false);
|
|
||||||
viewLocationExpanderContext1.Values = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "somekey1", "value1" },
|
|
||||||
{ "somekey2", "value2" },
|
|
||||||
};
|
|
||||||
|
|
||||||
var viewLocationExpanderContext2 = new ViewLocationExpanderContext(
|
|
||||||
GetActionContext("Controller1", "Area1"),
|
|
||||||
"View1",
|
|
||||||
isPartial: false);
|
|
||||||
viewLocationExpanderContext2.Values = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "somekey2", "value2" },
|
|
||||||
{ "somekey1", "value1" },
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var key1 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext1,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var key2 = DefaultViewLocationCache.GenerateKey(
|
|
||||||
viewLocationExpanderContext2,
|
|
||||||
copyViewExpanderValues: false);
|
|
||||||
|
|
||||||
var result = CacheKeyComparer.Equals(key1, key2);
|
|
||||||
var hash1 = CacheKeyComparer.GetHashCode(key1);
|
|
||||||
var hash2 = CacheKeyComparer.GetHashCode(key2);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.True(result);
|
|
||||||
Assert.Equal(hash1, hash2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ActionContext GetActionContext(
|
|
||||||
string controller = "mycontroller",
|
|
||||||
string area = null)
|
|
||||||
{
|
|
||||||
var routeData = new RouteData();
|
|
||||||
routeData.Values["controller"] = controller;
|
|
||||||
if (area != null)
|
|
||||||
{
|
|
||||||
routeData.Values["area"] = area;
|
|
||||||
}
|
|
||||||
|
|
||||||
var actionDesciptor = new ActionDescriptor();
|
|
||||||
actionDesciptor.RouteConstraints = new List<RouteDataActionConstraint>();
|
|
||||||
return new ActionContext(new DefaultHttpContext(), routeData, actionDesciptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ActionContext GetActionContextWithActionDescriptor(
|
|
||||||
IDictionary<string, object> routeValues,
|
|
||||||
IDictionary<string, string> routesInActionDescriptor,
|
|
||||||
bool isAttributeRouted)
|
|
||||||
{
|
|
||||||
var httpContext = new DefaultHttpContext();
|
|
||||||
var routeData = new RouteData();
|
|
||||||
foreach (var kvp in routeValues)
|
|
||||||
{
|
|
||||||
routeData.Values.Add(kvp.Key, kvp.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
var actionDescriptor = new ActionDescriptor();
|
|
||||||
if (isAttributeRouted)
|
|
||||||
{
|
|
||||||
actionDescriptor.AttributeRouteInfo = new Routing.AttributeRouteInfo();
|
|
||||||
foreach (var kvp in routesInActionDescriptor)
|
|
||||||
{
|
|
||||||
actionDescriptor.RouteValueDefaults.Add(kvp.Key, kvp.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
actionDescriptor.RouteConstraints = new List<RouteDataActionConstraint>();
|
|
||||||
foreach (var kvp in routesInActionDescriptor)
|
|
||||||
{
|
|
||||||
actionDescriptor.RouteConstraints.Add(new RouteDataActionConstraint(kvp.Key, kvp.Value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ActionContext(httpContext, routeData, actionDescriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -131,7 +131,12 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
IEnumerable<string> expectedViewLocations)
|
IEnumerable<string> expectedViewLocations)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var viewLocationExpanderContext = new ViewLocationExpanderContext(new ActionContext(),"testView", false);
|
var viewLocationExpanderContext = new ViewLocationExpanderContext(
|
||||||
|
new ActionContext(),
|
||||||
|
"testView",
|
||||||
|
"test-controller",
|
||||||
|
"",
|
||||||
|
false);
|
||||||
var languageViewLocationExpander = new LanguageViewLocationExpander(format);
|
var languageViewLocationExpander = new LanguageViewLocationExpander(format);
|
||||||
viewLocationExpanderContext.Values = new Dictionary<string, string>();
|
viewLocationExpanderContext.Values = new Dictionary<string, string>();
|
||||||
viewLocationExpanderContext.Values["language"] = "en-GB";
|
viewLocationExpanderContext.Values["language"] = "en-GB";
|
||||||
|
|
@ -150,7 +155,12 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
public void ExpandViewLocations_NullContextValue(IEnumerable<string> viewLocations)
|
public void ExpandViewLocations_NullContextValue(IEnumerable<string> viewLocations)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var viewLocationExpanderContext = new ViewLocationExpanderContext(new ActionContext(), "testView", false);
|
var viewLocationExpanderContext = new ViewLocationExpanderContext(
|
||||||
|
new ActionContext(),
|
||||||
|
"testView",
|
||||||
|
"test-controller",
|
||||||
|
"test-area",
|
||||||
|
false);
|
||||||
var languageViewLocationExpander = new LanguageViewLocationExpander();
|
var languageViewLocationExpander = new LanguageViewLocationExpander();
|
||||||
viewLocationExpanderContext.Values = new Dictionary<string, string>();
|
viewLocationExpanderContext.Values = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
|
@ -168,7 +178,12 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
public void ExpandViewLocations_IncorrectLocaleContextValue(IEnumerable<string> viewLocations)
|
public void ExpandViewLocations_IncorrectLocaleContextValue(IEnumerable<string> viewLocations)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var viewLocationExpanderContext = new ViewLocationExpanderContext(new ActionContext(), "testView", false);
|
var viewLocationExpanderContext = new ViewLocationExpanderContext(
|
||||||
|
new ActionContext(),
|
||||||
|
"testView",
|
||||||
|
"test-controller",
|
||||||
|
"test-area",
|
||||||
|
false);
|
||||||
var languageViewLocationExpander = new LanguageViewLocationExpander();
|
var languageViewLocationExpander = new LanguageViewLocationExpander();
|
||||||
viewLocationExpanderContext.Values = new Dictionary<string, string>();
|
viewLocationExpanderContext.Values = new Dictionary<string, string>();
|
||||||
viewLocationExpanderContext.Values["language"] = "!-invalid-culture-!";
|
viewLocationExpanderContext.Values["language"] = "!-invalid-culture-!";
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Threading;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
using Microsoft.AspNet.Mvc.Abstractions;
|
using Microsoft.AspNet.Mvc.Abstractions;
|
||||||
using Microsoft.AspNet.Mvc.Rendering;
|
|
||||||
using Microsoft.AspNet.Mvc.Routing;
|
using Microsoft.AspNet.Mvc.Routing;
|
||||||
using Microsoft.AspNet.Mvc.ViewEngines;
|
using Microsoft.AspNet.Mvc.ViewEngines;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.OptionsModel;
|
using Microsoft.Extensions.OptionsModel;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
using Microsoft.Extensions.WebEncoders.Testing;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -116,18 +119,24 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
public void FindPartialView_ReturnsRazorView_IfLookupWasSuccessful()
|
public void FindPartialView_ReturnsRazorView_IfLookupWasSuccessful()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var pageFactory = new Mock<IRazorPageFactory>();
|
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||||
var viewFactory = new Mock<IRazorViewFactory>();
|
|
||||||
var page = Mock.Of<IRazorPage>();
|
var page = Mock.Of<IRazorPage>();
|
||||||
var view = Mock.Of<IView>();
|
var viewStart1 = Mock.Of<IRazorPage>();
|
||||||
|
var viewStart2 = Mock.Of<IRazorPage>();
|
||||||
|
|
||||||
pageFactory.Setup(p => p.CreateInstance(It.IsAny<string>()))
|
pageFactory
|
||||||
.Returns(Mock.Of<IRazorPage>());
|
.Setup(p => p.CreateFactory("/Views/bar/test-view.cshtml"))
|
||||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorViewEngine>(), It.IsAny<IRazorPage>(), true))
|
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]));
|
||||||
.Returns(view)
|
|
||||||
.Verifiable();
|
|
||||||
|
|
||||||
var viewEngine = CreateViewEngine(pageFactory.Object, viewFactory.Object);
|
pageFactory
|
||||||
|
.Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => viewStart2, new IChangeToken[0]));
|
||||||
|
|
||||||
|
pageFactory
|
||||||
|
.Setup(p => p.CreateFactory("/_ViewStart.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => viewStart1, new IChangeToken[0]));
|
||||||
|
|
||||||
|
var viewEngine = CreateViewEngine(pageFactory.Object);
|
||||||
var context = GetActionContext(_controllerTestContext);
|
var context = GetActionContext(_controllerTestContext);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -135,9 +144,52 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result.Success);
|
Assert.True(result.Success);
|
||||||
Assert.Same(view, result.View);
|
var view = Assert.IsType<RazorView>(result.View);
|
||||||
|
Assert.Same(page, view.RazorPage);
|
||||||
Assert.Equal("test-view", result.ViewName);
|
Assert.Equal("test-view", result.ViewName);
|
||||||
viewFactory.Verify();
|
Assert.Empty(view.ViewStartPages);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FindPartialView_DoesNotExpireCachedResults_IfViewStartsExpire()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||||
|
var page = Mock.Of<IRazorPage>();
|
||||||
|
var viewStart = Mock.Of<IRazorPage>();
|
||||||
|
var cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
var changeToken = new CancellationChangeToken(cancellationTokenSource.Token);
|
||||||
|
|
||||||
|
pageFactory
|
||||||
|
.Setup(p => p.CreateFactory("/Views/bar/test-view.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]));
|
||||||
|
|
||||||
|
pageFactory
|
||||||
|
.Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => viewStart, new[] { changeToken }));
|
||||||
|
|
||||||
|
var viewEngine = CreateViewEngine(pageFactory.Object);
|
||||||
|
var context = GetActionContext(_controllerTestContext);
|
||||||
|
|
||||||
|
// Act - 1
|
||||||
|
var result1 = viewEngine.FindPartialView(context, "test-view");
|
||||||
|
|
||||||
|
// Assert - 1
|
||||||
|
Assert.True(result1.Success);
|
||||||
|
var view1 = Assert.IsType<RazorView>(result1.View);
|
||||||
|
Assert.Same(page, view1.RazorPage);
|
||||||
|
Assert.Equal("test-view", result1.ViewName);
|
||||||
|
Assert.Empty(view1.ViewStartPages);
|
||||||
|
|
||||||
|
// Act - 2
|
||||||
|
cancellationTokenSource.Cancel();
|
||||||
|
var result2 = viewEngine.FindPartialView(context, "test-view");
|
||||||
|
|
||||||
|
// Assert - 2
|
||||||
|
Assert.True(result2.Success);
|
||||||
|
var view2 = Assert.IsType<RazorView>(result2.View);
|
||||||
|
Assert.Same(page, view2.RazorPage);
|
||||||
|
pageFactory.Verify(p => p.CreateFactory("/Views/bar/test-view.cshtml"), Times.Once());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -150,8 +202,9 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
var context = GetActionContext(_controllerTestContext);
|
var context = GetActionContext(_controllerTestContext);
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
ExceptionAssert.ThrowsArgumentNullOrEmpty(() => viewEngine.FindPartialView(context, partialViewName),
|
ExceptionAssert.ThrowsArgumentNullOrEmpty(
|
||||||
"partialViewName");
|
() => viewEngine.FindPartialView(context, partialViewName),
|
||||||
|
"partialViewName");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -266,18 +319,24 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
public void FindView_ReturnsRazorView_IfLookupWasSuccessful()
|
public void FindView_ReturnsRazorView_IfLookupWasSuccessful()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var pageFactory = new Mock<IRazorPageFactory>();
|
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||||
var viewFactory = new Mock<IRazorViewFactory>();
|
|
||||||
var page = Mock.Of<IRazorPage>();
|
var page = Mock.Of<IRazorPage>();
|
||||||
var view = Mock.Of<IView>();
|
var viewStart1 = Mock.Of<IRazorPage>();
|
||||||
|
var viewStart2 = Mock.Of<IRazorPage>();
|
||||||
|
|
||||||
pageFactory.Setup(p => p.CreateInstance(It.IsAny<string>()))
|
pageFactory
|
||||||
.Returns(Mock.Of<IRazorPage>());
|
.Setup(p => p.CreateFactory("/Views/bar/test-view.cshtml"))
|
||||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorViewEngine>(), It.IsAny<IRazorPage>(), false))
|
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]));
|
||||||
.Returns(view)
|
|
||||||
.Verifiable();
|
|
||||||
|
|
||||||
var viewEngine = CreateViewEngine(pageFactory.Object, viewFactory.Object);
|
pageFactory
|
||||||
|
.Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => viewStart2, new IChangeToken[0]));
|
||||||
|
|
||||||
|
pageFactory
|
||||||
|
.Setup(p => p.CreateFactory("/_ViewStart.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => viewStart1, new IChangeToken[0]));
|
||||||
|
|
||||||
|
var viewEngine = CreateViewEngine(pageFactory.Object);
|
||||||
var context = GetActionContext(_controllerTestContext);
|
var context = GetActionContext(_controllerTestContext);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -285,63 +344,66 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result.Success);
|
Assert.True(result.Success);
|
||||||
Assert.Same(view, result.View);
|
var view = Assert.IsType<RazorView>(result.View);
|
||||||
Assert.Equal("test-view", result.ViewName);
|
Assert.Equal("test-view", result.ViewName);
|
||||||
viewFactory.Verify();
|
Assert.Same(page, view.RazorPage);
|
||||||
|
Assert.False(view.IsPartial);
|
||||||
|
Assert.Equal(new[] { viewStart1, viewStart2 }, view.ViewStartPages);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void FindView_UsesViewLocationFormat_IfRouteDoesNotContainArea()
|
public void FindView_UsesViewLocationFormat_IfRouteDoesNotContainArea()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var pageFactory = new Mock<IRazorPageFactory>();
|
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||||
var viewFactory = new Mock<IRazorViewFactory>();
|
|
||||||
var page = Mock.Of<IRazorPage>();
|
var page = Mock.Of<IRazorPage>();
|
||||||
pageFactory
|
pageFactory
|
||||||
.Setup(p => p.CreateInstance("fake-path1/bar/test-view.rzr"))
|
.Setup(p => p.CreateFactory("fake-path1/bar/test-view.rzr"))
|
||||||
.Returns(page)
|
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var viewEngine = new OverloadedLocationViewEngine(
|
var viewEngine = new TestableRazorViewEngine(
|
||||||
pageFactory.Object,
|
pageFactory.Object,
|
||||||
viewFactory.Object,
|
GetOptionsAccessor());
|
||||||
GetOptionsAccessor(),
|
viewEngine.SetLocationFormats(
|
||||||
GetViewLocationCache());
|
new[] { "fake-path1/{1}/{0}.rzr" },
|
||||||
|
new[] { "fake-area-path/{2}/{1}/{0}.rzr" });
|
||||||
var context = GetActionContext(_controllerTestContext);
|
var context = GetActionContext(_controllerTestContext);
|
||||||
viewFactory.Setup(v => v.GetView(viewEngine, page, false))
|
|
||||||
.Returns(Mock.Of<IView>());
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = viewEngine.FindView(context, "test-view");
|
var result = viewEngine.FindView(context, "test-view");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
pageFactory.Verify();
|
pageFactory.Verify();
|
||||||
|
var view = Assert.IsType<RazorView>(result.View);
|
||||||
|
Assert.Same(page, view.RazorPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void FindView_UsesAreaViewLocationFormat_IfRouteContainsArea()
|
public void FindView_UsesAreaViewLocationFormat_IfRouteContainsArea()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var pageFactory = new Mock<IRazorPageFactory>();
|
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||||
var viewFactory = new Mock<IRazorViewFactory>();
|
|
||||||
var page = Mock.Of<IRazorPage>();
|
var page = Mock.Of<IRazorPage>();
|
||||||
pageFactory
|
pageFactory
|
||||||
.Setup(p => p.CreateInstance("fake-area-path/foo/bar/test-view2.rzr"))
|
.Setup(p => p.CreateFactory("fake-area-path/foo/bar/test-view2.rzr"))
|
||||||
.Returns(page)
|
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var viewEngine = new OverloadedLocationViewEngine(
|
var viewEngine = new TestableRazorViewEngine(
|
||||||
pageFactory.Object,
|
pageFactory.Object,
|
||||||
viewFactory.Object,
|
GetOptionsAccessor());
|
||||||
GetOptionsAccessor(),
|
viewEngine.SetLocationFormats(
|
||||||
GetViewLocationCache());
|
new[] { "fake-path1/{1}/{0}.rzr" },
|
||||||
|
new[] { "fake-area-path/{2}/{1}/{0}.rzr" });
|
||||||
var context = GetActionContext(_areaTestContext);
|
var context = GetActionContext(_areaTestContext);
|
||||||
viewFactory.Setup(v => v.GetView(viewEngine, page, false))
|
|
||||||
.Returns(Mock.Of<IView>());
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = viewEngine.FindView(context, "test-view2");
|
var result = viewEngine.FindView(context, "test-view2");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
pageFactory.Verify();
|
pageFactory.Verify();
|
||||||
|
var view = Assert.IsType<RazorView>(result.View);
|
||||||
|
Assert.Same(page, view.RazorPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -351,46 +413,49 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
IEnumerable<string> expectedSeeds)
|
IEnumerable<string> expectedSeeds)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var pageFactory = new Mock<IRazorPageFactory>();
|
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||||
pageFactory.Setup(p => p.CreateInstance("test-string/bar.cshtml"))
|
pageFactory
|
||||||
.Returns(Mock.Of<IRazorPage>())
|
.Setup(p => p.CreateFactory("test-string/bar.cshtml"))
|
||||||
.Verifiable();
|
.Returns(new RazorPageFactoryResult(() => Mock.Of<IRazorPage>(), new IChangeToken[0]))
|
||||||
|
.Verifiable();
|
||||||
var viewFactory = new Mock<IRazorViewFactory>();
|
|
||||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorViewEngine>(), It.IsAny<IRazorPage>(), It.IsAny<bool>()))
|
|
||||||
.Returns(Mock.Of<IView>());
|
|
||||||
|
|
||||||
var expander1Result = new[] { "some-seed" };
|
var expander1Result = new[] { "some-seed" };
|
||||||
var expander1 = new Mock<IViewLocationExpander>();
|
var expander1 = new Mock<IViewLocationExpander>();
|
||||||
expander1.Setup(e => e.PopulateValues(It.IsAny<ViewLocationExpanderContext>()))
|
expander1
|
||||||
.Callback((ViewLocationExpanderContext c) =>
|
.Setup(e => e.PopulateValues(It.IsAny<ViewLocationExpanderContext>()))
|
||||||
{
|
.Callback((ViewLocationExpanderContext c) =>
|
||||||
Assert.NotNull(c.ActionContext);
|
{
|
||||||
c.Values["expander-key"] = expander1.ToString();
|
Assert.NotNull(c.ActionContext);
|
||||||
})
|
c.Values["expander-key"] = expander1.ToString();
|
||||||
.Verifiable();
|
})
|
||||||
expander1.Setup(e => e.ExpandViewLocations(It.IsAny<ViewLocationExpanderContext>(),
|
.Verifiable();
|
||||||
It.IsAny<IEnumerable<string>>()))
|
expander1
|
||||||
.Callback((ViewLocationExpanderContext c, IEnumerable<string> seeds) =>
|
.Setup(e => e.ExpandViewLocations(
|
||||||
{
|
It.IsAny<ViewLocationExpanderContext>(),
|
||||||
Assert.NotNull(c.ActionContext);
|
It.IsAny<IEnumerable<string>>()))
|
||||||
Assert.Equal(expectedSeeds, seeds);
|
.Callback((ViewLocationExpanderContext c, IEnumerable<string> seeds) =>
|
||||||
})
|
{
|
||||||
.Returns(expander1Result)
|
Assert.NotNull(c.ActionContext);
|
||||||
.Verifiable();
|
Assert.Equal(expectedSeeds, seeds);
|
||||||
|
})
|
||||||
|
.Returns(expander1Result)
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
var expander2 = new Mock<IViewLocationExpander>();
|
var expander2 = new Mock<IViewLocationExpander>();
|
||||||
expander2.Setup(e => e.ExpandViewLocations(It.IsAny<ViewLocationExpanderContext>(),
|
expander2
|
||||||
It.IsAny<IEnumerable<string>>()))
|
.Setup(e => e.ExpandViewLocations(
|
||||||
.Callback((ViewLocationExpanderContext c, IEnumerable<string> seeds) =>
|
It.IsAny<ViewLocationExpanderContext>(),
|
||||||
{
|
It.IsAny<IEnumerable<string>>()))
|
||||||
Assert.Equal(expander1Result, seeds);
|
.Callback((ViewLocationExpanderContext c, IEnumerable<string> seeds) =>
|
||||||
})
|
{
|
||||||
.Returns(new[] { "test-string/{1}.cshtml" })
|
Assert.Equal(expander1Result, seeds);
|
||||||
.Verifiable();
|
})
|
||||||
|
.Returns(new[] { "test-string/{1}.cshtml" })
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
var viewEngine = CreateViewEngine(pageFactory.Object, viewFactory.Object,
|
var viewEngine = CreateViewEngine(
|
||||||
new[] { expander1.Object, expander2.Object });
|
pageFactory.Object,
|
||||||
|
new[] { expander1.Object, expander2.Object });
|
||||||
var context = GetActionContext(routeValues);
|
var context = GetActionContext(routeValues);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -408,130 +473,152 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
public void FindView_CachesValuesIfViewWasFound()
|
public void FindView_CachesValuesIfViewWasFound()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var pageFactory = new Mock<IRazorPageFactory>();
|
var page = Mock.Of<IRazorPage>();
|
||||||
pageFactory.Setup(p => p.CreateInstance("/Views/bar/baz.cshtml"))
|
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||||
.Verifiable();
|
pageFactory
|
||||||
pageFactory.Setup(p => p.CreateInstance("/Views/Shared/baz.cshtml"))
|
.Setup(p => p.CreateFactory("/Views/bar/baz.cshtml"))
|
||||||
.Returns(Mock.Of<IRazorPage>())
|
.Returns(new RazorPageFactoryResult(new IChangeToken[0]))
|
||||||
.Verifiable();
|
|
||||||
|
|
||||||
var viewFactory = new Mock<IRazorViewFactory>();
|
|
||||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorViewEngine>(), It.IsAny<IRazorPage>(), false))
|
|
||||||
.Returns(Mock.Of<IView>());
|
|
||||||
|
|
||||||
var cache = GetViewLocationCache();
|
|
||||||
var cacheMock = Mock.Get(cache);
|
|
||||||
cacheMock.Setup(c => c.Set(It.IsAny<ViewLocationExpanderContext>(), It.IsAny<ViewLocationCacheResult>()))
|
|
||||||
.Callback((ViewLocationExpanderContext _, ViewLocationCacheResult cacheResult) =>
|
|
||||||
{
|
|
||||||
Assert.Equal("/Views/Shared/baz.cshtml", cacheResult.ViewLocation);
|
|
||||||
Assert.Equal(new[] { "/Views/bar/baz.cshtml" }, cacheResult.SearchedLocations);
|
|
||||||
})
|
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
|
pageFactory
|
||||||
|
.Setup(p => p.CreateFactory("/Views/Shared/baz.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
var viewEngine = CreateViewEngine(pageFactory.Object, viewFactory.Object, cache: cache);
|
var viewEngine = CreateViewEngine(pageFactory.Object);
|
||||||
var context = GetActionContext(_controllerTestContext);
|
var context = GetActionContext(_controllerTestContext);
|
||||||
|
|
||||||
// Act
|
// Act 1
|
||||||
var result = viewEngine.FindView(context, "baz");
|
var result1 = viewEngine.FindView(context, "baz");
|
||||||
|
|
||||||
// Assert
|
// Assert 1
|
||||||
Assert.True(result.Success);
|
Assert.True(result1.Success);
|
||||||
|
var view1 = Assert.IsType<RazorView>(result1.View);
|
||||||
|
Assert.Same(page, view1.RazorPage);
|
||||||
|
pageFactory.Verify();
|
||||||
|
|
||||||
|
// Act 2
|
||||||
|
pageFactory
|
||||||
|
.Setup(p => p.CreateFactory(It.IsAny<string>()))
|
||||||
|
.Throws(new Exception("Shouldn't be called"));
|
||||||
|
|
||||||
|
var result2 = viewEngine.FindView(context, "baz");
|
||||||
|
|
||||||
|
// Assert 2
|
||||||
|
Assert.True(result2.Success);
|
||||||
|
var view2 = Assert.IsType<RazorView>(result2.View);
|
||||||
|
Assert.Same(page, view2.RazorPage);
|
||||||
pageFactory.Verify();
|
pageFactory.Verify();
|
||||||
cacheMock.Verify();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void FindView_UsesCachedValueIfViewWasFound()
|
public void FindView_InvokesPageFactoryIfChangeTokenExpired()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var pageFactory = new Mock<IRazorPageFactory>(MockBehavior.Strict);
|
var page1 = Mock.Of<IRazorPage>();
|
||||||
pageFactory.Setup(p => p.CreateInstance("some-view-location"))
|
var page2 = Mock.Of<IRazorPage>();
|
||||||
.Returns(Mock.Of<IRazorPage>())
|
var sequence = new MockSequence();
|
||||||
.Verifiable();
|
var cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
var changeToken = new CancellationChangeToken(cancellationTokenSource.Token);
|
||||||
|
|
||||||
var viewFactory = new Mock<IRazorViewFactory>();
|
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorViewEngine>(), It.IsAny<IRazorPage>(), false))
|
pageFactory
|
||||||
.Returns(Mock.Of<IView>());
|
.InSequence(sequence)
|
||||||
|
.Setup(p => p.CreateFactory("/Views/bar/baz.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(new[] { changeToken }));
|
||||||
|
pageFactory
|
||||||
|
.InSequence(sequence)
|
||||||
|
.Setup(p => p.CreateFactory("/Views/Shared/baz.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => page1, new IChangeToken[0]))
|
||||||
|
.Verifiable();
|
||||||
|
pageFactory
|
||||||
|
.InSequence(sequence)
|
||||||
|
.Setup(p => p.CreateFactory("/Views/bar/baz.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => page2, new IChangeToken[0]));
|
||||||
|
|
||||||
var expander = new Mock<IViewLocationExpander>(MockBehavior.Strict);
|
var viewEngine = CreateViewEngine(pageFactory.Object);
|
||||||
expander.Setup(v => v.PopulateValues(It.IsAny<ViewLocationExpanderContext>()))
|
|
||||||
.Verifiable();
|
|
||||||
var cacheMock = new Mock<IViewLocationCache>();
|
|
||||||
cacheMock.Setup(c => c.Get(It.IsAny<ViewLocationExpanderContext>()))
|
|
||||||
.Returns(new ViewLocationCacheResult("some-view-location", Enumerable.Empty<string>()))
|
|
||||||
.Verifiable();
|
|
||||||
|
|
||||||
var viewEngine = CreateViewEngine(pageFactory.Object,
|
|
||||||
viewFactory.Object,
|
|
||||||
new[] { expander.Object },
|
|
||||||
cacheMock.Object);
|
|
||||||
var context = GetActionContext(_controllerTestContext);
|
var context = GetActionContext(_controllerTestContext);
|
||||||
|
|
||||||
// Act
|
// Act 1
|
||||||
var result = viewEngine.FindView(context, "baz");
|
var result1 = viewEngine.FindView(context, "baz");
|
||||||
|
|
||||||
// Assert
|
// Assert 1
|
||||||
Assert.True(result.Success);
|
Assert.True(result1.Success);
|
||||||
|
var view1 = Assert.IsType<RazorView>(result1.View);
|
||||||
|
Assert.Same(page1, view1.RazorPage);
|
||||||
|
|
||||||
|
// Act 2
|
||||||
|
cancellationTokenSource.Cancel();
|
||||||
|
var result2 = viewEngine.FindView(context, "baz");
|
||||||
|
|
||||||
|
// Assert 2
|
||||||
|
Assert.True(result2.Success);
|
||||||
|
var view2 = Assert.IsType<RazorView>(result2.View);
|
||||||
|
Assert.Same(page2, view2.RazorPage);
|
||||||
pageFactory.Verify();
|
pageFactory.Verify();
|
||||||
cacheMock.Verify();
|
|
||||||
expander.Verify();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void FindView_LooksForViewsIfCachedViewDoesNotExist()
|
public void FindView_InvokesPageFactoryIfViewStartExpirationTokensHaveExpired()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var pageFactory = new Mock<IRazorPageFactory>();
|
var page1 = Mock.Of<IRazorPage>();
|
||||||
pageFactory.Setup(p => p.CreateInstance("expired-location"))
|
var page2 = Mock.Of<IRazorPage>();
|
||||||
.Returns((IRazorPage)null)
|
var viewStart = Mock.Of<IRazorPage>();
|
||||||
.Verifiable();
|
var sequence = new MockSequence();
|
||||||
pageFactory.Setup(p => p.CreateInstance("some-view-location"))
|
var cancellationTokenSource = new CancellationTokenSource();
|
||||||
.Returns(Mock.Of<IRazorPage>())
|
var changeToken = new CancellationChangeToken(cancellationTokenSource.Token);
|
||||||
.Verifiable();
|
|
||||||
|
|
||||||
var viewFactory = new Mock<IRazorViewFactory>();
|
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorViewEngine>(), It.IsAny<IRazorPage>(), false))
|
pageFactory
|
||||||
.Returns(Mock.Of<IView>());
|
.InSequence(sequence)
|
||||||
|
.Setup(p => p.CreateFactory("/Views/bar/baz.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => page1, new IChangeToken[0]));
|
||||||
|
pageFactory
|
||||||
|
.InSequence(sequence)
|
||||||
|
.Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(new[] { changeToken }))
|
||||||
|
.Verifiable();
|
||||||
|
pageFactory
|
||||||
|
.InSequence(sequence)
|
||||||
|
.Setup(p => p.CreateFactory("/Views/bar/baz.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => page2, new IChangeToken[0]));
|
||||||
|
pageFactory
|
||||||
|
.InSequence(sequence)
|
||||||
|
.Setup(p => p.CreateFactory("/Views/_ViewStart.cshtml"))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => viewStart, new IChangeToken[0]));
|
||||||
|
|
||||||
var cacheMock = new Mock<IViewLocationCache>();
|
var viewEngine = CreateViewEngine(pageFactory.Object);
|
||||||
cacheMock.Setup(c => c.Get(It.IsAny<ViewLocationExpanderContext>()))
|
|
||||||
.Returns(new ViewLocationCacheResult("expired-location", Enumerable.Empty<string>()));
|
|
||||||
|
|
||||||
var expander = new Mock<IViewLocationExpander>();
|
|
||||||
expander.Setup(v => v.PopulateValues(It.IsAny<ViewLocationExpanderContext>()))
|
|
||||||
.Verifiable();
|
|
||||||
var expanderResult = new[] { "some-view-location" };
|
|
||||||
expander.Setup(v => v.ExpandViewLocations(
|
|
||||||
It.IsAny<ViewLocationExpanderContext>(), It.IsAny<IEnumerable<string>>()))
|
|
||||||
.Returns((ViewLocationExpanderContext c, IEnumerable<string> seed) => expanderResult)
|
|
||||||
.Verifiable();
|
|
||||||
|
|
||||||
var viewEngine = CreateViewEngine(pageFactory.Object,
|
|
||||||
viewFactory.Object,
|
|
||||||
expanders: new[] { expander.Object },
|
|
||||||
cache: cacheMock.Object);
|
|
||||||
var context = GetActionContext(_controllerTestContext);
|
var context = GetActionContext(_controllerTestContext);
|
||||||
|
|
||||||
// Act
|
// Act 1
|
||||||
var result = viewEngine.FindView(context, "baz");
|
var result1 = viewEngine.FindView(context, "baz");
|
||||||
|
|
||||||
// Assert
|
// Assert 1
|
||||||
Assert.True(result.Success);
|
Assert.True(result1.Success);
|
||||||
|
var view1 = Assert.IsType<RazorView>(result1.View);
|
||||||
|
Assert.Same(page1, view1.RazorPage);
|
||||||
|
Assert.Empty(view1.ViewStartPages);
|
||||||
|
|
||||||
|
// Act 2
|
||||||
|
cancellationTokenSource.Cancel();
|
||||||
|
var result2 = viewEngine.FindView(context, "baz");
|
||||||
|
|
||||||
|
// Assert 2
|
||||||
|
Assert.True(result2.Success);
|
||||||
|
var view2 = Assert.IsType<RazorView>(result2.View);
|
||||||
|
Assert.Same(page2, view2.RazorPage);
|
||||||
|
var actualViewStart = Assert.Single(view2.ViewStartPages);
|
||||||
|
Assert.Equal(viewStart, actualViewStart);
|
||||||
pageFactory.Verify();
|
pageFactory.Verify();
|
||||||
cacheMock.Verify();
|
|
||||||
expander.Verify();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This test validates an important perf scenario of RazorViewEngine not constructing
|
// This test validates an important perf scenario of RazorViewEngine not constructing
|
||||||
// multiple strings for views that do not exist in the file system on a per-request basis.
|
// multiple strings for views that do not exist in the file system on a per-request basis.
|
||||||
[Fact]
|
[Fact]
|
||||||
public void FindView_DoesNotInvokeExpandViewLocations_IfCacheEntryMatchesButViewIsNotFound()
|
public void FindView_DoesNotInvokeViewLocationExpanders_IfChangeTokenHasNotExpired()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var pageFactory = Mock.Of<IRazorPageFactory>();
|
var pageFactory = Mock.Of<IRazorPageFactoryProvider>();
|
||||||
var viewFactory = Mock.Of<IRazorViewFactory>();
|
|
||||||
var cache = new DefaultViewLocationCache();
|
|
||||||
var expander = new Mock<IViewLocationExpander>();
|
var expander = new Mock<IViewLocationExpander>();
|
||||||
var expandedLocations = new[]
|
var expandedLocations = new[]
|
||||||
{
|
{
|
||||||
|
|
@ -548,16 +635,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
expander
|
expander
|
||||||
.Setup(v => v.ExpandViewLocations(
|
.Setup(v => v.ExpandViewLocations(
|
||||||
It.IsAny<ViewLocationExpanderContext>(), It.IsAny<IEnumerable<string>>()))
|
It.IsAny<ViewLocationExpanderContext>(),
|
||||||
.Returns((ViewLocationExpanderContext c, IEnumerable<string> viewLocations) => expandedLocations)
|
It.IsAny<IEnumerable<string>>()))
|
||||||
|
.Returns(expandedLocations)
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
|
|
||||||
|
|
||||||
var viewEngine = CreateViewEngine(
|
var viewEngine = CreateViewEngine(
|
||||||
pageFactory,
|
pageFactory,
|
||||||
viewFactory,
|
expanders: new[] { expander.Object });
|
||||||
expanders: new[] { expander.Object },
|
|
||||||
cache: cache);
|
|
||||||
var context = GetActionContext(_controllerTestContext);
|
var context = GetActionContext(_controllerTestContext);
|
||||||
|
|
||||||
// Act - 1
|
// Act - 1
|
||||||
|
|
@ -582,6 +667,70 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
Times.Once());
|
Times.Once());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FindView_InvokesViewLocationExpanders_IfChangeTokenExpires()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
var changeToken = new CancellationChangeToken(cancellationTokenSource.Token);
|
||||||
|
var page = Mock.Of<IRazorPage>();
|
||||||
|
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||||
|
pageFactory
|
||||||
|
.Setup(p => p.CreateFactory("viewlocation3"))
|
||||||
|
.Returns(new RazorPageFactoryResult(new[] { changeToken }));
|
||||||
|
var expander = new Mock<IViewLocationExpander>();
|
||||||
|
var expandedLocations = new[]
|
||||||
|
{
|
||||||
|
"viewlocation1",
|
||||||
|
"viewlocation2",
|
||||||
|
"viewlocation3",
|
||||||
|
};
|
||||||
|
expander
|
||||||
|
.Setup(v => v.PopulateValues(It.IsAny<ViewLocationExpanderContext>()))
|
||||||
|
.Callback((ViewLocationExpanderContext expanderContext) =>
|
||||||
|
{
|
||||||
|
expanderContext.Values["somekey"] = "somevalue";
|
||||||
|
})
|
||||||
|
.Verifiable();
|
||||||
|
expander
|
||||||
|
.Setup(v => v.ExpandViewLocations(
|
||||||
|
It.IsAny<ViewLocationExpanderContext>(),
|
||||||
|
It.IsAny<IEnumerable<string>>()))
|
||||||
|
.Returns(expandedLocations)
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
var viewEngine = CreateViewEngine(
|
||||||
|
pageFactory.Object,
|
||||||
|
expanders: new[] { expander.Object });
|
||||||
|
var context = GetActionContext(_controllerTestContext);
|
||||||
|
|
||||||
|
// Act - 1
|
||||||
|
var result = viewEngine.FindView(context, "MyView");
|
||||||
|
|
||||||
|
// Assert - 1
|
||||||
|
Assert.False(result.Success);
|
||||||
|
Assert.Equal(expandedLocations, result.SearchedLocations);
|
||||||
|
expander.Verify();
|
||||||
|
|
||||||
|
// Act - 2
|
||||||
|
pageFactory
|
||||||
|
.Setup(p => p.CreateFactory("viewlocation3"))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]));
|
||||||
|
cancellationTokenSource.Cancel();
|
||||||
|
result = viewEngine.FindView(context, "MyView");
|
||||||
|
|
||||||
|
// Assert - 2
|
||||||
|
Assert.True(result.Success);
|
||||||
|
var view = Assert.IsType<RazorView>(result.View);
|
||||||
|
Assert.Same(page, view.RazorPage);
|
||||||
|
expander.Verify(
|
||||||
|
v => v.PopulateValues(It.IsAny<ViewLocationExpanderContext>()),
|
||||||
|
Times.Exactly(2));
|
||||||
|
expander.Verify(
|
||||||
|
v => v.ExpandViewLocations(It.IsAny<ViewLocationExpanderContext>(), It.IsAny<IEnumerable<string>>()),
|
||||||
|
Times.Exactly(2));
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(null)]
|
[InlineData(null)]
|
||||||
[InlineData("")]
|
[InlineData("")]
|
||||||
|
|
@ -604,36 +753,39 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var page = Mock.Of<IRazorPage>();
|
var page = Mock.Of<IRazorPage>();
|
||||||
var pageFactory = new Mock<IRazorPageFactory>();
|
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||||
pageFactory.Setup(p => p.CreateInstance("expanded-path/bar-layout"))
|
pageFactory
|
||||||
.Returns(page)
|
.Setup(p => p.CreateFactory("expanded-path/bar-layout"))
|
||||||
.Verifiable();
|
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||||
|
.Verifiable();
|
||||||
var viewFactory = new Mock<IRazorViewFactory>(MockBehavior.Strict);
|
|
||||||
|
|
||||||
var expander = new Mock<IViewLocationExpander>();
|
var expander = new Mock<IViewLocationExpander>();
|
||||||
expander.Setup(e => e.PopulateValues(It.IsAny<ViewLocationExpanderContext>()))
|
expander
|
||||||
.Callback((ViewLocationExpanderContext c) =>
|
.Setup(e => e.PopulateValues(It.IsAny<ViewLocationExpanderContext>()))
|
||||||
{
|
.Callback((ViewLocationExpanderContext c) =>
|
||||||
Assert.NotNull(c.ActionContext);
|
{
|
||||||
c.Values["expander-key"] = expander.ToString();
|
Assert.NotNull(c.ActionContext);
|
||||||
})
|
c.Values["expander-key"] = expander.ToString();
|
||||||
.Verifiable();
|
})
|
||||||
expander.Setup(e => e.ExpandViewLocations(It.IsAny<ViewLocationExpanderContext>(),
|
.Verifiable();
|
||||||
It.IsAny<IEnumerable<string>>()))
|
expander
|
||||||
.Returns((ViewLocationExpanderContext c, IEnumerable<string> seeds) =>
|
.Setup(e => e.ExpandViewLocations(
|
||||||
{
|
It.IsAny<ViewLocationExpanderContext>(),
|
||||||
Assert.NotNull(c.ActionContext);
|
It.IsAny<IEnumerable<string>>()))
|
||||||
Assert.Equal(expectedSeeds, seeds);
|
.Returns((ViewLocationExpanderContext c, IEnumerable<string> seeds) =>
|
||||||
|
{
|
||||||
|
Assert.NotNull(c.ActionContext);
|
||||||
|
Assert.Equal(expectedSeeds, seeds);
|
||||||
|
|
||||||
Assert.Equal(expander.ToString(), c.Values["expander-key"]);
|
Assert.Equal(expander.ToString(), c.Values["expander-key"]);
|
||||||
|
|
||||||
return new[] { "expanded-path/bar-{0}" };
|
return new[] { "expanded-path/bar-{0}" };
|
||||||
})
|
})
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
|
|
||||||
var viewEngine = CreateViewEngine(pageFactory.Object, viewFactory.Object,
|
var viewEngine = CreateViewEngine(
|
||||||
new[] { expander.Object });
|
pageFactory.Object,
|
||||||
|
new[] { expander.Object });
|
||||||
var context = GetActionContext(routeValues);
|
var context = GetActionContext(routeValues);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -686,10 +838,11 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
};
|
};
|
||||||
|
|
||||||
var page = new Mock<IRazorPage>(MockBehavior.Strict).Object;
|
var page = new Mock<IRazorPage>(MockBehavior.Strict).Object;
|
||||||
var pageFactory = new Mock<IRazorPageFactory>();
|
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||||
pageFactory.Setup(p => p.CreateInstance("/Views/Foo/details.cshtml"))
|
pageFactory
|
||||||
.Returns(page)
|
.Setup(p => p.CreateFactory("/Views/Foo/details.cshtml"))
|
||||||
.Verifiable();
|
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
var viewEngine = CreateViewEngine(pageFactory.Object);
|
var viewEngine = CreateViewEngine(pageFactory.Object);
|
||||||
var routesInActionDescriptor = new Dictionary<string, string>()
|
var routesInActionDescriptor = new Dictionary<string, string>()
|
||||||
|
|
@ -1092,23 +1245,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private RazorViewEngine CreateViewEngine(
|
private TestableRazorViewEngine CreateViewEngine(
|
||||||
IRazorPageFactory pageFactory = null,
|
IRazorPageFactoryProvider pageFactory = null,
|
||||||
IRazorViewFactory viewFactory = null,
|
IEnumerable<IViewLocationExpander> expanders = null)
|
||||||
IEnumerable<IViewLocationExpander> expanders = null,
|
|
||||||
IViewLocationCache cache = null)
|
|
||||||
{
|
{
|
||||||
pageFactory = pageFactory ?? Mock.Of<IRazorPageFactory>();
|
pageFactory = pageFactory ?? Mock.Of<IRazorPageFactoryProvider>();
|
||||||
viewFactory = viewFactory ?? Mock.Of<IRazorViewFactory>();
|
return new TestableRazorViewEngine(
|
||||||
|
pageFactory,
|
||||||
cache = cache ?? GetViewLocationCache();
|
GetOptionsAccessor(expanders));
|
||||||
|
|
||||||
var viewEngine = new RazorViewEngine(pageFactory,
|
|
||||||
viewFactory,
|
|
||||||
GetOptionsAccessor(expanders),
|
|
||||||
cache);
|
|
||||||
|
|
||||||
return viewEngine;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IOptions<RazorViewEngineOptions> GetOptionsAccessor(
|
private static IOptions<RazorViewEngineOptions> GetOptionsAccessor(
|
||||||
|
|
@ -1129,15 +1273,6 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
return optionsAccessor.Object;
|
return optionsAccessor.Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IViewLocationCache GetViewLocationCache()
|
|
||||||
{
|
|
||||||
var cacheMock = new Mock<IViewLocationCache>();
|
|
||||||
cacheMock.Setup(c => c.Get(It.IsAny<ViewLocationExpanderContext>()))
|
|
||||||
.Returns<string>(null);
|
|
||||||
|
|
||||||
return cacheMock.Object;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ActionContext GetActionContext(IDictionary<string, object> routeValues)
|
private static ActionContext GetActionContext(IDictionary<string, object> routeValues)
|
||||||
{
|
{
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
|
|
@ -1185,32 +1320,33 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
||||||
return new ActionContext(httpContext, routeData, actionDescriptor);
|
return new ActionContext(httpContext, routeData, actionDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class OverloadedLocationViewEngine : RazorViewEngine
|
private class TestableRazorViewEngine : RazorViewEngine
|
||||||
{
|
{
|
||||||
public OverloadedLocationViewEngine(
|
private IEnumerable<string> _viewLocationFormats;
|
||||||
IRazorPageFactory pageFactory,
|
private IEnumerable<string> _areaViewLocationFormats;
|
||||||
IRazorViewFactory viewFactory,
|
|
||||||
IOptions<RazorViewEngineOptions> optionsAccessor,
|
public TestableRazorViewEngine(
|
||||||
IViewLocationCache cache)
|
IRazorPageFactoryProvider pageFactory,
|
||||||
: base(pageFactory, viewFactory, optionsAccessor, cache)
|
IOptions<RazorViewEngineOptions> optionsAccessor)
|
||||||
|
: base(pageFactory, Mock.Of<IRazorPageActivator>(), new HtmlTestEncoder(), optionsAccessor)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> ViewLocationFormats
|
public void SetLocationFormats(
|
||||||
|
IEnumerable<string> viewLocationFormats,
|
||||||
|
IEnumerable<string> areaViewLocationFormats)
|
||||||
{
|
{
|
||||||
get
|
_viewLocationFormats = viewLocationFormats;
|
||||||
{
|
_areaViewLocationFormats = areaViewLocationFormats;
|
||||||
return new[] { "fake-path1/{1}/{0}.rzr" };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<string> AreaViewLocationFormats
|
public override IEnumerable<string> ViewLocationFormats =>
|
||||||
{
|
_viewLocationFormats != null ? _viewLocationFormats : base.ViewLocationFormats;
|
||||||
get
|
|
||||||
{
|
public override IEnumerable<string> AreaViewLocationFormats =>
|
||||||
return new[] { "fake-area-path/{2}/{1}/{0}.rzr" };
|
_areaViewLocationFormats != null ? _areaViewLocationFormats : base.AreaViewLocationFormats;
|
||||||
}
|
|
||||||
}
|
public IMemoryCache ViewLookupCachePublic => ViewLookupCache;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,55 +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 Microsoft.Extensions.WebEncoders.Testing;
|
|
||||||
using Moq;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Razor
|
|
||||||
{
|
|
||||||
public class RazorViewFactoryTest
|
|
||||||
{
|
|
||||||
[Theory]
|
|
||||||
[InlineData(false)]
|
|
||||||
[InlineData(true)]
|
|
||||||
public void GetView_SetsIsPartial(bool isPartial)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var factory = new RazorViewFactory(
|
|
||||||
Mock.Of<IRazorPageActivator>(),
|
|
||||||
Mock.Of<IViewStartProvider>(),
|
|
||||||
new HtmlTestEncoder());
|
|
||||||
var page = Mock.Of<IRazorPage>();
|
|
||||||
var viewEngine = Mock.Of<IRazorViewEngine>();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var view = factory.GetView(viewEngine, page, isPartial);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var razorView = Assert.IsType<RazorView>(view);
|
|
||||||
Assert.Same(page, razorView.RazorPage);
|
|
||||||
Assert.Equal(razorView.IsPartial, isPartial);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetView_SetsRazorPage()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var factory = new RazorViewFactory(
|
|
||||||
Mock.Of<IRazorPageActivator>(),
|
|
||||||
Mock.Of<IViewStartProvider>(),
|
|
||||||
new HtmlTestEncoder());
|
|
||||||
|
|
||||||
var page = Mock.Of<IRazorPage>();
|
|
||||||
var viewEngine = Mock.Of<IRazorViewEngine>();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var view = factory.GetView(viewEngine, page, isPartial: false);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotNull(view);
|
|
||||||
var razorView = Assert.IsType<RazorView>(view);
|
|
||||||
Assert.Same(razorView.RazorPage, page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http.Features;
|
using Microsoft.AspNet.Http.Features;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
|
|
@ -14,6 +13,7 @@ using Microsoft.AspNet.Mvc.Rendering;
|
||||||
using Microsoft.AspNet.Mvc.ViewFeatures;
|
using Microsoft.AspNet.Mvc.ViewFeatures;
|
||||||
using Microsoft.AspNet.PageExecutionInstrumentation;
|
using Microsoft.AspNet.PageExecutionInstrumentation;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
using Microsoft.Extensions.WebEncoders.Testing;
|
using Microsoft.Extensions.WebEncoders.Testing;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -39,12 +39,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
v.HtmlEncoder = new HtmlTestEncoder();
|
v.HtmlEncoder = new HtmlTestEncoder();
|
||||||
v.Write("Hello world");
|
v.Write("Hello world");
|
||||||
});
|
});
|
||||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
var view = new RazorView(
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorViewEngine>(),
|
||||||
CreateViewStartProvider(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
page,
|
new IRazorPage[0],
|
||||||
new HtmlTestEncoder(),
|
page,
|
||||||
isPartial: true);
|
new HtmlTestEncoder(),
|
||||||
|
isPartial: true);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
var expected = viewContext.Writer;
|
var expected = viewContext.Writer;
|
||||||
|
|
||||||
|
|
@ -68,12 +69,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
Assert.Same(viewData, v.ViewContext.ViewData);
|
Assert.Same(viewData, v.ViewContext.ViewData);
|
||||||
});
|
});
|
||||||
var activator = new Mock<IRazorPageActivator>();
|
var activator = new Mock<IRazorPageActivator>();
|
||||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
var view = new RazorView(
|
||||||
activator.Object,
|
Mock.Of<IRazorViewEngine>(),
|
||||||
CreateViewStartProvider(),
|
activator.Object,
|
||||||
page,
|
new IRazorPage[0],
|
||||||
new HtmlTestEncoder(),
|
page,
|
||||||
isPartial: true);
|
new HtmlTestEncoder(),
|
||||||
|
isPartial: true);
|
||||||
|
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
var expectedWriter = viewContext.Writer;
|
var expectedWriter = viewContext.Writer;
|
||||||
|
|
@ -132,12 +134,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
var viewEngine = new Mock<IRazorViewEngine>();
|
var viewEngine = new Mock<IRazorViewEngine>();
|
||||||
viewEngine.Setup(v => v.FindPage(It.IsAny<ActionContext>(), LayoutPath))
|
viewEngine.Setup(v => v.FindPage(It.IsAny<ActionContext>(), LayoutPath))
|
||||||
.Returns(new RazorPageResult(LayoutPath, layout));
|
.Returns(new RazorPageResult(LayoutPath, layout));
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(
|
||||||
activator,
|
viewEngine.Object,
|
||||||
CreateViewStartProvider(viewStart),
|
activator,
|
||||||
page,
|
new[] { viewStart },
|
||||||
new HtmlTestEncoder(),
|
page,
|
||||||
isPartial: false);
|
new HtmlTestEncoder(),
|
||||||
|
isPartial: false);
|
||||||
|
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
var expectedWriter = viewContext.Writer;
|
var expectedWriter = viewContext.Writer;
|
||||||
|
|
@ -157,12 +160,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
var activator = new Mock<IRazorPageActivator>();
|
var activator = new Mock<IRazorPageActivator>();
|
||||||
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
|
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
var view = new RazorView(
|
||||||
activator.Object,
|
Mock.Of<IRazorViewEngine>(),
|
||||||
CreateViewStartProvider(),
|
activator.Object,
|
||||||
page,
|
new IRazorPage[0],
|
||||||
new HtmlTestEncoder(),
|
page,
|
||||||
isPartial: true);
|
new HtmlTestEncoder(),
|
||||||
|
isPartial: true);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -193,29 +197,28 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
v.Write("layout-content" + Environment.NewLine);
|
v.Write("layout-content" + Environment.NewLine);
|
||||||
v.RenderBodyPublic();
|
v.RenderBodyPublic();
|
||||||
});
|
});
|
||||||
var pageFactory = new Mock<IRazorPageFactory>();
|
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||||
pageFactory.Setup(p => p.CreateInstance(LayoutPath))
|
pageFactory
|
||||||
.Returns(layout);
|
.Setup(p => p.CreateFactory(LayoutPath))
|
||||||
|
.Returns(new RazorPageFactoryResult(() => layout, new IChangeToken[0]));
|
||||||
|
|
||||||
var viewEngine = new Mock<IRazorViewEngine>();
|
var viewEngine = new Mock<IRazorViewEngine>();
|
||||||
viewEngine.Setup(v => v.FindPage(It.IsAny<ActionContext>(), LayoutPath))
|
viewEngine.Setup(v => v.FindPage(It.IsAny<ActionContext>(), LayoutPath))
|
||||||
.Returns(new RazorPageResult(LayoutPath, layout));
|
.Returns(new RazorPageResult(LayoutPath, layout));
|
||||||
|
|
||||||
var viewStartProvider = CreateViewStartProvider();
|
var view = new RazorView(
|
||||||
var view = new RazorView(viewEngine.Object,
|
viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
viewStartProvider,
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: true);
|
isPartial: true);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await view.RenderAsync(viewContext);
|
await view.RenderAsync(viewContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Mock.Get(viewStartProvider)
|
|
||||||
.Verify(v => v.GetViewStartPages(It.IsAny<string>()), Times.Never());
|
|
||||||
Assert.Equal(expected, viewContext.Writer.ToString());
|
Assert.Equal(expected, viewContext.Writer.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,12 +231,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
actual = v.Output;
|
actual = v.Output;
|
||||||
});
|
});
|
||||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
var view = new RazorView(
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorViewEngine>(),
|
||||||
CreateViewStartProvider(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
page,
|
new IRazorPage[0],
|
||||||
new HtmlTestEncoder(),
|
page,
|
||||||
isPartial: false);
|
new HtmlTestEncoder(),
|
||||||
|
isPartial: false);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
var original = viewContext.Writer;
|
var original = viewContext.Writer;
|
||||||
|
|
||||||
|
|
@ -253,12 +257,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
{
|
{
|
||||||
v.WriteLiteral("Hello world");
|
v.WriteLiteral("Hello world");
|
||||||
});
|
});
|
||||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
var view = new RazorView(
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorViewEngine>(),
|
||||||
CreateViewStartProvider(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
page,
|
new IRazorPage[0],
|
||||||
new HtmlTestEncoder(),
|
page,
|
||||||
isPartial: false);
|
new HtmlTestEncoder(),
|
||||||
|
isPartial: false);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
var original = viewContext.Writer;
|
var original = viewContext.Writer;
|
||||||
|
|
||||||
|
|
@ -280,12 +285,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
var activator = new Mock<IRazorPageActivator>();
|
var activator = new Mock<IRazorPageActivator>();
|
||||||
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
|
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
var view = new RazorView(
|
||||||
activator.Object,
|
Mock.Of<IRazorViewEngine>(),
|
||||||
CreateViewStartProvider(),
|
activator.Object,
|
||||||
page,
|
new IRazorPage[0],
|
||||||
new HtmlTestEncoder(),
|
page,
|
||||||
isPartial: false);
|
new HtmlTestEncoder(),
|
||||||
|
isPartial: false);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -323,12 +329,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
|
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
var view = new RazorView(
|
||||||
activator.Object,
|
Mock.Of<IRazorViewEngine>(),
|
||||||
CreateViewStartProvider(viewStart1, viewStart2),
|
activator.Object,
|
||||||
page,
|
new[] { viewStart1, viewStart2 },
|
||||||
new HtmlTestEncoder(),
|
page,
|
||||||
isPartial: false);
|
new HtmlTestEncoder(),
|
||||||
|
isPartial: false);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -358,7 +365,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
var activator = new Mock<IRazorPageActivator>();
|
var activator = new Mock<IRazorPageActivator>();
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
Mock.Of<IViewStartProvider>(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -422,7 +429,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(viewEngine.Object,
|
||||||
activator.Object,
|
activator.Object,
|
||||||
CreateViewStartProvider(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -464,7 +471,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -527,7 +534,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -587,7 +594,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -647,7 +654,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -712,7 +719,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -744,7 +751,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -808,7 +815,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -844,7 +851,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -889,7 +896,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -956,7 +963,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -1009,7 +1016,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -1059,7 +1066,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -1089,7 +1096,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -1134,7 +1141,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -1204,13 +1211,9 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
var viewEngine = new Mock<IRazorViewEngine>();
|
var viewEngine = new Mock<IRazorViewEngine>();
|
||||||
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "Layout"))
|
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "Layout"))
|
||||||
.Returns(new RazorPageResult("Layout", layout));
|
.Returns(new RazorPageResult("Layout", layout));
|
||||||
var viewStartProvider = new Mock<IViewStartProvider>();
|
|
||||||
viewStartProvider.Setup(v => v.GetViewStartPages(It.IsAny<string>()))
|
|
||||||
.Returns(Enumerable.Empty<IRazorPage>())
|
|
||||||
.Verifiable();
|
|
||||||
var view = new RazorView(viewEngine.Object,
|
var view = new RazorView(viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
viewStartProvider.Object,
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -1222,7 +1225,6 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
feature.Verify();
|
feature.Verify();
|
||||||
viewStartProvider.Verify();
|
|
||||||
Assert.True(layoutExecuted);
|
Assert.True(layoutExecuted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1256,7 +1258,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
Mock.Of<IViewStartProvider>(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: true);
|
isPartial: true);
|
||||||
|
|
@ -1288,7 +1290,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
Mock.Of<IViewStartProvider>(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial);
|
isPartial);
|
||||||
|
|
@ -1328,7 +1330,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
|
|
||||||
var view = new RazorView(viewEngine,
|
var view = new RazorView(viewEngine,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(viewStart1, viewStart2),
|
new[] { viewStart1, viewStart2 },
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -1364,12 +1366,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
});
|
});
|
||||||
var viewEngine = Mock.Of<IRazorViewEngine>();
|
var viewEngine = Mock.Of<IRazorViewEngine>();
|
||||||
|
|
||||||
var view = new RazorView(viewEngine,
|
var view = new RazorView(
|
||||||
Mock.Of<IRazorPageActivator>(),
|
viewEngine,
|
||||||
CreateViewStartProvider(viewStart1, viewStart2),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
page,
|
new[] { viewStart1, viewStart2 },
|
||||||
new HtmlTestEncoder(),
|
page,
|
||||||
isPartial: false);
|
new HtmlTestEncoder(),
|
||||||
|
isPartial: false);
|
||||||
var viewContext = CreateViewContext(view);
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -1402,13 +1405,14 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
v.RenderBodyPublic();
|
v.RenderBodyPublic();
|
||||||
});
|
});
|
||||||
var viewEngine = new Mock<IRazorViewEngine>();
|
var viewEngine = new Mock<IRazorViewEngine>();
|
||||||
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "/Layout.cshtml"))
|
viewEngine
|
||||||
.Returns(new RazorPageResult("Layout", layout));
|
.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "/Layout.cshtml"))
|
||||||
|
.Returns(new RazorPageResult("Layout", layout));
|
||||||
|
|
||||||
var view = new RazorView(
|
var view = new RazorView(
|
||||||
viewEngine.Object,
|
viewEngine.Object,
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(viewStart),
|
new[] { viewStart },
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: false);
|
isPartial: false);
|
||||||
|
|
@ -1434,7 +1438,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
});
|
});
|
||||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||||
Mock.Of<IRazorPageActivator>(),
|
Mock.Of<IRazorPageActivator>(),
|
||||||
CreateViewStartProvider(),
|
new IRazorPage[0],
|
||||||
page,
|
page,
|
||||||
new HtmlTestEncoder(),
|
new HtmlTestEncoder(),
|
||||||
isPartial: true);
|
isPartial: true);
|
||||||
|
|
@ -1447,6 +1451,48 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
Assert.True(isPartialPage.Value);
|
Assert.True(isPartialPage.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RenderAsync_RendersViewStartsInOrderInWhichTheyAreSpecified()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = string.Join(
|
||||||
|
Environment.NewLine,
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
"ViewStart1",
|
||||||
|
"ViewStart2",
|
||||||
|
"Page",
|
||||||
|
});
|
||||||
|
var page = new TestableRazorPage(v =>
|
||||||
|
{
|
||||||
|
v.WriteLiteral("Page");
|
||||||
|
});
|
||||||
|
var viewStart1 = new TestableRazorPage(v =>
|
||||||
|
{
|
||||||
|
v.WriteLiteral("ViewStart1" + Environment.NewLine);
|
||||||
|
});
|
||||||
|
var viewStart2 = new TestableRazorPage(v =>
|
||||||
|
{
|
||||||
|
v.WriteLiteral("ViewStart2" + Environment.NewLine);
|
||||||
|
});
|
||||||
|
var viewEngine = Mock.Of<IRazorViewEngine>();
|
||||||
|
|
||||||
|
var view = new RazorView(
|
||||||
|
viewEngine,
|
||||||
|
Mock.Of<IRazorPageActivator>(),
|
||||||
|
new[] { viewStart1, viewStart2 },
|
||||||
|
page,
|
||||||
|
new HtmlTestEncoder(),
|
||||||
|
isPartial: false);
|
||||||
|
var viewContext = CreateViewContext(view);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await view.RenderAsync(viewContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, viewContext.Writer.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
private static TextWriter CreateBufferedWriter()
|
private static TextWriter CreateBufferedWriter()
|
||||||
{
|
{
|
||||||
var mockWriter = new Mock<TextWriter>();
|
var mockWriter = new Mock<TextWriter>();
|
||||||
|
|
@ -1469,17 +1515,6 @@ namespace Microsoft.AspNet.Mvc.Razor
|
||||||
new HtmlHelperOptions());
|
new HtmlHelperOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IViewStartProvider CreateViewStartProvider(params IRazorPage[] viewStartPages)
|
|
||||||
{
|
|
||||||
viewStartPages = viewStartPages ?? new IRazorPage[0];
|
|
||||||
var viewStartProvider = new Mock<IViewStartProvider>();
|
|
||||||
viewStartProvider
|
|
||||||
.Setup(v => v.GetViewStartPages(It.IsAny<string>()))
|
|
||||||
.Returns(viewStartPages);
|
|
||||||
|
|
||||||
return viewStartProvider.Object;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestableRazorPage : RazorPage
|
private class TestableRazorPage : RazorPage
|
||||||
{
|
{
|
||||||
private readonly Action<TestableRazorPage> _executeAction;
|
private readonly Action<TestableRazorPage> _executeAction;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue