parent
845b86963e
commit
380a93d370
|
|
@ -10,83 +10,63 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
/// <summary>
|
||||
/// Represents the result of compilation.
|
||||
/// </summary>
|
||||
public class CompilationResult
|
||||
public struct CompilationResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="CompilationResult"/>.
|
||||
/// </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.
|
||||
/// Initializes a new instance of <see cref="CompilationResult"/> for a successful compilation.
|
||||
/// </summary>
|
||||
/// <param name="type">The compiled type.</param>
|
||||
/// <returns>A <see cref="CompilationResult"/> instance for a successful compilation.</returns>
|
||||
public static CompilationResult Successful(Type type)
|
||||
public CompilationResult(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
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 Microsoft.AspNet.FileProviders;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
||||
{
|
||||
|
|
@ -56,7 +57,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -95,33 +96,29 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
string normalizedPath,
|
||||
Func<RelativeFileInfo, CompilationResult> compile)
|
||||
{
|
||||
CompilerCacheResult cacheResult;
|
||||
var fileInfo = _fileProvider.GetFileInfo(normalizedPath);
|
||||
MemoryCacheEntryOptions cacheEntryOptions;
|
||||
CompilerCacheResult cacheResultToCache;
|
||||
CompilerCacheResult cacheResult;
|
||||
if (!fileInfo.Exists)
|
||||
{
|
||||
cacheResultToCache = CompilerCacheResult.FileNotFound;
|
||||
cacheResult = CompilerCacheResult.FileNotFound;
|
||||
var expirationToken = _fileProvider.Watch(normalizedPath);
|
||||
cacheResult = new CompilerCacheResult(new[] { expirationToken });
|
||||
|
||||
cacheEntryOptions = new MemoryCacheEntryOptions();
|
||||
cacheEntryOptions.AddExpirationToken(_fileProvider.Watch(normalizedPath));
|
||||
cacheEntryOptions.AddExpirationToken(expirationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
var relativeFileInfo = new RelativeFileInfo(fileInfo, normalizedPath);
|
||||
var compilationResult = compile(relativeFileInfo).EnsureSuccessful();
|
||||
var compilationResult = compile(relativeFileInfo);
|
||||
compilationResult.EnsureSuccessful();
|
||||
cacheEntryOptions = GetMemoryCacheEntryOptions(normalizedPath);
|
||||
|
||||
// By default the CompilationResult returned by IRoslynCompiler is an instance of
|
||||
// UncachedCompilationResult. This type has the generated code as a string property and do not want
|
||||
// to cache it. We'll instead cache the unwrapped result.
|
||||
cacheResultToCache = new CompilerCacheResult(
|
||||
CompilationResult.Successful(compilationResult.CompiledType));
|
||||
cacheResult = new CompilerCacheResult(compilationResult);
|
||||
cacheResult = new CompilerCacheResult(
|
||||
compilationResult,
|
||||
cacheEntryOptions.ExpirationTokens);
|
||||
}
|
||||
|
||||
_cache.Set(normalizedPath, cacheResultToCache, cacheEntryOptions);
|
||||
_cache.Set(normalizedPath, cacheResult, cacheEntryOptions);
|
||||
return cacheResult;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,46 +2,77 @@
|
|||
// 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.Compilation
|
||||
{
|
||||
/// <summary>
|
||||
/// Result of <see cref="ICompilerCache"/>.
|
||||
/// </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>
|
||||
/// Initializes a new instance of <see cref="CompilerCacheResult"/> with the specified
|
||||
/// <see cref="CompilationResult"/>.
|
||||
/// <see cref="Compilation.CompilationResult"/>.
|
||||
/// </summary>
|
||||
/// <param name="compilationResult">The <see cref="Compilation.CompilationResult"/> </param>
|
||||
/// <param name="compilationResult">The <see cref="Compilation.CompilationResult"/>.</param>
|
||||
public CompilerCacheResult(CompilationResult compilationResult)
|
||||
: this(compilationResult, new IChangeToken[0])
|
||||
{
|
||||
if (compilationResult == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(compilationResult));
|
||||
}
|
||||
|
||||
CompilationResult = compilationResult;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
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>
|
||||
/// The <see cref="Compilation.CompilationResult"/>.
|
||||
/// </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; }
|
||||
|
||||
/// <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);
|
||||
}
|
||||
|
||||
return CompilationResult.Failed(failures);
|
||||
return new CompilationResult(failures);
|
||||
}
|
||||
|
||||
private DiagnosticMessage CreateDiagnosticMessage(RazorError error, string filePath)
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
var type = assembly.GetExportedTypes()
|
||||
.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);
|
||||
}
|
||||
|
||||
return CompilationResult.Failed(failures);
|
||||
return new CompilationResult(failures);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
/// <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.
|
||||
/// </summary>
|
||||
public class VirtualPathRazorPageFactory : IRazorPageFactory
|
||||
public class DefaultRazorPageFactoryProvider : IRazorPageFactoryProvider
|
||||
{
|
||||
/// <remarks>
|
||||
/// This delegate holds on to an instance of <see cref="IRazorCompilationService"/>.
|
||||
|
|
@ -20,11 +20,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
private ICompilerCache _compilerCache;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="VirtualPathRazorPageFactory"/>.
|
||||
/// Initializes a new instance of <see cref="DefaultRazorPageFactoryProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="razorCompilationService">The <see cref="IRazorCompilationService"/>.</param>
|
||||
/// <param name="compilerCacheProvider">The <see cref="ICompilerCacheProvider"/>.</param>
|
||||
public VirtualPathRazorPageFactory(
|
||||
public DefaultRazorPageFactoryProvider(
|
||||
IRazorCompilationService razorCompilationService,
|
||||
ICompilerCacheProvider compilerCacheProvider)
|
||||
{
|
||||
|
|
@ -46,7 +46,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRazorPage CreateInstance(string relativePath)
|
||||
public RazorPageFactoryResult CreateFactory(string relativePath)
|
||||
{
|
||||
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.
|
||||
relativePath = relativePath.Substring(1);
|
||||
}
|
||||
|
||||
var result = CompilerCache.GetOrAdd(relativePath, _compileDelegate);
|
||||
|
||||
if (result == CompilerCacheResult.FileNotFound)
|
||||
if (result.Success)
|
||||
{
|
||||
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);
|
||||
page.Path = relativePath;
|
||||
|
||||
return page;
|
||||
/// <summary>
|
||||
/// Creates a factory for <see cref="IRazorPage"/>.
|
||||
/// </summary>
|
||||
/// <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>();
|
||||
|
||||
// Caches view locations that are valid for the lifetime of the application.
|
||||
services.TryAddSingleton<IViewLocationCache, DefaultViewLocationCache>();
|
||||
services.TryAdd(ServiceDescriptor.Singleton<IChunkTreeCache>(serviceProvider =>
|
||||
{
|
||||
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
|
||||
// creating the singleton RazorViewEngine instance.
|
||||
services.TryAddTransient<IRazorViewFactory, RazorViewFactory>();
|
||||
services.TryAddTransient<IRazorPageFactory, VirtualPathRazorPageFactory>();
|
||||
services.TryAddTransient<IRazorPageFactoryProvider, DefaultRazorPageFactoryProvider>();
|
||||
services.TryAddTransient<IRazorCompilationService, RazorCompilationService>();
|
||||
services.TryAddTransient<IViewStartProvider, ViewStartProvider>();
|
||||
services.TryAddTransient<IMvcRazorHost, MvcRazorHost>();
|
||||
|
||||
// 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>
|
||||
/// Defines methods that are used for creating <see cref="IRazorPage"/> instances at a given path.
|
||||
/// </summary>
|
||||
public interface IRazorPageFactory
|
||||
public interface IRazorPageFactoryProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a <see cref="IRazorPage"/> for the specified path.
|
||||
/// Creates a <see cref="IRazorPage"/> factory for the specified path.
|
||||
/// </summary>
|
||||
/// <param name="relativePath">The path to locate the page.</param>
|
||||
/// <returns>The IRazorPage instance if it exists, null otherwise.</returns>
|
||||
IRazorPage CreateInstance(string relativePath);
|
||||
/// <returns>The <see cref="RazorPageFactoryResult"/> instance.</returns>
|
||||
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 GeneratePrecompiledAssembly(syntaxTrees.Where(tree => tree != null),
|
||||
razorFiles.Where(file => file != null));
|
||||
return GeneratePrecompiledAssembly(
|
||||
syntaxTrees.Where(tree => tree != null),
|
||||
razorFiles.Where(file => file != null));
|
||||
}
|
||||
|
||||
protected virtual RazorFileInfoCollection GeneratePrecompiledAssembly(
|
||||
|
|
@ -174,13 +175,15 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation
|
|||
var references = CompileContext.Compilation.References
|
||||
.Concat(new[] { applicationReference });
|
||||
|
||||
var preCompilationOptions = CompilationSettings.CompilationOptions
|
||||
.WithOutputKind(OutputKind.DynamicallyLinkedLibrary);
|
||||
var preCompilationOptions = CompilationSettings
|
||||
.CompilationOptions
|
||||
.WithOutputKind(OutputKind.DynamicallyLinkedLibrary);
|
||||
|
||||
var compilation = CSharpCompilation.Create(assemblyResourceName,
|
||||
options: preCompilationOptions,
|
||||
syntaxTrees: syntaxTrees,
|
||||
references: references);
|
||||
var compilation = CSharpCompilation.Create(
|
||||
assemblyResourceName,
|
||||
options: preCompilationOptions,
|
||||
syntaxTrees: syntaxTrees,
|
||||
references: references);
|
||||
|
||||
var generateSymbols = GenerateSymbols && SymbolsUtility.SupportsSymbolsGeneration();
|
||||
// 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)
|
||||
{
|
||||
var syntaxTree = SyntaxTreeGenerator.Generate(results.GeneratedCode,
|
||||
fileInfo.FileInfo.PhysicalPath,
|
||||
CompilationSettings);
|
||||
var syntaxTree = SyntaxTreeGenerator.Generate(
|
||||
results.GeneratedCode,
|
||||
fileInfo.FileInfo.PhysicalPath,
|
||||
CompilationSettings);
|
||||
var fullTypeName = results.GetMainClassName(host, syntaxTree);
|
||||
|
||||
if (fullTypeName != null)
|
||||
|
|
@ -297,9 +301,10 @@ namespace Microsoft.AspNet.Mvc.Razor.Precompilation
|
|||
}
|
||||
else
|
||||
{
|
||||
var diagnostics = results.ParserErrors
|
||||
.Select(error => error.ToDiagnostics(fileInfo.FileInfo.PhysicalPath))
|
||||
.ToList();
|
||||
var diagnostics = results
|
||||
.ParserErrors
|
||||
.Select(error => error.ToDiagnostics(fileInfo.FileInfo.PhysicalPath))
|
||||
.ToList();
|
||||
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the results of locating a <see cref="IRazorPage"/>.
|
||||
/// Result of locating a <see cref="IRazorPage"/>.
|
||||
/// </summary>
|
||||
public class RazorPageResult
|
||||
public struct RazorPageResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="RazorPageResult"/> for a successful discovery.
|
||||
|
|
@ -30,6 +30,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
Name = name;
|
||||
Page = page;
|
||||
SearchedLocations = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -50,6 +51,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
|
||||
Name = name;
|
||||
Page = null;
|
||||
SearchedLocations = searchedLocations;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
private readonly IRazorViewEngine _viewEngine;
|
||||
private readonly IRazorPageActivator _pageActivator;
|
||||
private readonly IViewStartProvider _viewStartProvider;
|
||||
private readonly HtmlEncoder _htmlEncoder;
|
||||
private IPageExecutionListenerFeature _pageExecutionFeature;
|
||||
|
||||
|
|
@ -31,22 +30,22 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// </summary>
|
||||
/// <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="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="htmlEncoder">The HTML encoder.</param>
|
||||
/// <param name="isPartial">Determines if the view is to be executed as a partial.</param>
|
||||
/// pages</param>
|
||||
public RazorView(
|
||||
IRazorViewEngine viewEngine,
|
||||
IRazorPageActivator pageActivator,
|
||||
IViewStartProvider viewStartProvider,
|
||||
IReadOnlyList<IRazorPage> viewStartPages,
|
||||
IRazorPage razorPage,
|
||||
HtmlEncoder htmlEncoder,
|
||||
bool isPartial)
|
||||
{
|
||||
_viewEngine = viewEngine;
|
||||
_pageActivator = pageActivator;
|
||||
_viewStartProvider = viewStartProvider;
|
||||
ViewStartPages = viewStartPages;
|
||||
RazorPage = razorPage;
|
||||
_htmlEncoder = htmlEncoder;
|
||||
IsPartial = isPartial;
|
||||
|
|
@ -68,6 +67,12 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// </summary>
|
||||
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
|
||||
{
|
||||
get { return _pageExecutionFeature != null; }
|
||||
|
|
@ -153,14 +158,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
private async Task RenderViewStartAsync(ViewContext context)
|
||||
{
|
||||
var viewStarts = _viewStartProvider.GetViewStartPages(RazorPage.Path);
|
||||
|
||||
string layout = null;
|
||||
var oldFilePath = context.ExecutingFilePath;
|
||||
try
|
||||
{
|
||||
foreach (var viewStart in viewStarts)
|
||||
for (var i = 0; i < ViewStartPages.Count; i++)
|
||||
{
|
||||
var viewStart = ViewStartPages[i];
|
||||
context.ExecutingFilePath = viewStart.Path;
|
||||
// Copy the layout value from the previous view start (if any) to the current.
|
||||
viewStart.Layout = layout;
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.AspNet.Mvc.Routing;
|
||||
using Microsoft.AspNet.Mvc.ViewEngines;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.OptionsModel;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
|
|
@ -22,41 +24,34 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
public class RazorViewEngine : IRazorViewEngine
|
||||
{
|
||||
private const string ViewExtension = ".cshtml";
|
||||
internal const string ControllerKey = "controller";
|
||||
internal const string AreaKey = "area";
|
||||
private const string ControllerKey = "controller";
|
||||
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[]
|
||||
{
|
||||
"/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 IRazorPageFactoryProvider _pageFactory;
|
||||
private readonly IList<IViewLocationExpander> _viewLocationExpanders;
|
||||
private readonly IViewLocationCache _viewLocationCache;
|
||||
private readonly IRazorPageActivator _pageActivator;
|
||||
private readonly HtmlEncoder _htmlEncoder;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RazorViewEngine" /> class.
|
||||
/// Initializes a new instance of the <see cref="RazorViewEngine" />.
|
||||
/// </summary>
|
||||
/// <param name="pageFactory">The page factory used for creating <see cref="IRazorPage"/> instances.</param>
|
||||
public RazorViewEngine(
|
||||
IRazorPageFactory pageFactory,
|
||||
IRazorViewFactory viewFactory,
|
||||
IOptions<RazorViewEngineOptions> optionsAccessor,
|
||||
IViewLocationCache viewLocationCache)
|
||||
IRazorPageFactoryProvider pageFactory,
|
||||
IRazorPageActivator pageActivator,
|
||||
HtmlEncoder htmlEncoder,
|
||||
IOptions<RazorViewEngineOptions> optionsAccessor)
|
||||
{
|
||||
_pageFactory = pageFactory;
|
||||
_viewFactory = viewFactory;
|
||||
_pageActivator = pageActivator;
|
||||
_viewLocationExpanders = optionsAccessor.Value.ViewLocationExpanders;
|
||||
_viewLocationCache = viewLocationCache;
|
||||
_htmlEncoder = htmlEncoder;
|
||||
ViewLookupCache = new MemoryCache(new MemoryCacheOptions
|
||||
{
|
||||
CompactOnMemoryPressure = false
|
||||
});
|
||||
}
|
||||
|
||||
/// <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
|
||||
/// <c>/Views/Home/Test.cshtml</c>. Locations such as <c>/views/home/test.cshtml</c> would not be discovered
|
||||
/// </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>
|
||||
/// 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
|
||||
/// <c>/Views/Home/Test.cshtml</c>. Locations such as <c>/views/home/test.cshtml</c> would not be discovered
|
||||
/// </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 />
|
||||
public ViewEngineResult FindView(
|
||||
ActionContext context,
|
||||
string viewName)
|
||||
public ViewEngineResult FindView(ActionContext context, string viewName)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
|
|
@ -112,14 +113,12 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(viewName));
|
||||
}
|
||||
|
||||
var pageResult = GetRazorPageResult(context, viewName, isPartial: false);
|
||||
return CreateViewEngineResult(pageResult, _viewFactory, isPartial: false);
|
||||
var pageResult = GetViewLocationCacheResult(context, viewName, isPartial: false);
|
||||
return CreateViewEngineResult(pageResult, viewName, isPartial: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ViewEngineResult FindPartialView(
|
||||
ActionContext context,
|
||||
string partialViewName)
|
||||
public ViewEngineResult FindPartialView(ActionContext context, string partialViewName)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
|
|
@ -131,14 +130,12 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(partialViewName));
|
||||
}
|
||||
|
||||
var pageResult = GetRazorPageResult(context, partialViewName, isPartial: true);
|
||||
return CreateViewEngineResult(pageResult, _viewFactory, isPartial: true);
|
||||
var pageResult = GetViewLocationCacheResult(context, partialViewName, isPartial: true);
|
||||
return CreateViewEngineResult(pageResult, partialViewName, isPartial: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RazorPageResult FindPage(
|
||||
ActionContext context,
|
||||
string pageName)
|
||||
public RazorPageResult FindPage(ActionContext context, string pageName)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
|
|
@ -150,7 +147,16 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
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>
|
||||
|
|
@ -166,9 +172,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// <see cref="Abstractions.ActionDescriptor.RouteConstraints"/> for traditional routes to get route values
|
||||
/// produces consistently cased results.
|
||||
/// </remarks>
|
||||
public static string GetNormalizedRouteValue(
|
||||
ActionContext context,
|
||||
string key)
|
||||
public static string GetNormalizedRouteValue(ActionContext context, string key)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
|
|
@ -228,26 +232,14 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
return stringRouteValue;
|
||||
}
|
||||
|
||||
private RazorPageResult GetRazorPageResult(
|
||||
private ViewLocationCacheResult GetViewLocationCacheResult(
|
||||
ActionContext context,
|
||||
string pageName,
|
||||
bool isPartial)
|
||||
{
|
||||
if (IsApplicationRelativePath(pageName))
|
||||
{
|
||||
var applicationRelativePath = pageName;
|
||||
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 });
|
||||
return LocatePageFromPath(pageName, isPartial);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -255,105 +247,221 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
}
|
||||
|
||||
private RazorPageResult LocatePageFromViewLocations(
|
||||
ActionContext context,
|
||||
private ViewLocationCacheResult LocatePageFromPath(string pageName, bool isPartial)
|
||||
{
|
||||
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,
|
||||
bool isPartial)
|
||||
{
|
||||
// Initialize the dictionary for the typical case of having controller and action tokens.
|
||||
var areaName = GetNormalizedRouteValue(context, AreaKey);
|
||||
var controllerName = GetNormalizedRouteValue(actionContext, ControllerKey);
|
||||
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)
|
||||
{
|
||||
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
|
||||
for( var i = 0; i < _viewLocationExpanders.Count; i++)
|
||||
for (var i = 0; i < _viewLocationExpanders.Count; i++)
|
||||
{
|
||||
_viewLocationExpanders[i].PopulateValues(expanderContext);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. With the values that we've accumumlated so far, check if we have a cached result.
|
||||
IEnumerable<string> locationsToSearch = null;
|
||||
var cachedResult = _viewLocationCache.Get(expanderContext);
|
||||
if (!cachedResult.Equals(ViewLocationCacheResult.None))
|
||||
{
|
||||
if (cachedResult.IsFoundResult)
|
||||
{
|
||||
var page = _pageFactory.CreateInstance(cachedResult.ViewLocation);
|
||||
var cacheKey = new ViewLocationCacheKey(
|
||||
expanderContext.ViewName,
|
||||
expanderContext.ControllerName,
|
||||
expanderContext.ViewName,
|
||||
expanderContext.IsPartial,
|
||||
expanderValues);
|
||||
|
||||
if (page != null)
|
||||
{
|
||||
// 2a We have a cache entry where a view was previously found.
|
||||
return new RazorPageResult(pageName, page);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
locationsToSearch = cachedResult.SearchedLocations;
|
||||
}
|
||||
ViewLocationCacheResult cacheResult;
|
||||
if (!ViewLookupCache.TryGetValue(cacheKey, out cacheResult))
|
||||
{
|
||||
cacheResult = OnCacheMiss(expanderContext, cacheKey);
|
||||
}
|
||||
|
||||
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.
|
||||
// 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
|
||||
));
|
||||
viewLocations = _viewLocationExpanders[i].ExpandViewLocations(expanderContext, viewLocations);
|
||||
}
|
||||
|
||||
// 3. Use the expanded locations to look up a page.
|
||||
ViewLocationCacheResult cacheResult = null;
|
||||
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);
|
||||
if (page != null)
|
||||
var path = string.Format(
|
||||
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.
|
||||
_viewLocationCache.Set(expanderContext, new ViewLocationCacheResult(path, searchedLocations));
|
||||
return new RazorPageResult(pageName, page);
|
||||
break;
|
||||
}
|
||||
|
||||
searchedLocations.Add(path);
|
||||
}
|
||||
|
||||
// 3b. We did not find a page for any of the paths.
|
||||
_viewLocationCache.Set(expanderContext, new ViewLocationCacheResult(searchedLocations));
|
||||
return new RazorPageResult(pageName, searchedLocations);
|
||||
// No views were found at the specified location. Create a not found result.
|
||||
if (cacheResult == null)
|
||||
{
|
||||
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(
|
||||
RazorPageResult result,
|
||||
IRazorViewFactory razorViewFactory,
|
||||
ViewLocationCacheResult result,
|
||||
string viewName,
|
||||
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);
|
||||
return ViewEngineResult.Found(result.Name, view);
|
||||
var page = result.ViewEntry.PageFactory();
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
/// <summary>
|
||||
/// Result of <see cref="IViewLocationCache"/> lookups.
|
||||
/// Result of view location cache lookup.
|
||||
/// </summary>
|
||||
public struct ViewLocationCacheResult : IEquatable<ViewLocationCacheResult>
|
||||
public class ViewLocationCacheResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="ViewLocationCacheResult"/>
|
||||
/// for a view that was successfully found at the specified location.
|
||||
/// </summary>
|
||||
/// <param name="foundLocation">The view location.</param>
|
||||
/// <param name="searchedLocations">Locations that were searched
|
||||
/// in addition to <paramref name="foundLocation"/>.</param>
|
||||
/// <param name="view">The <see cref="ViewLocationCacheItem"/> for the found view.</param>
|
||||
/// <param name="viewStarts"><see cref="ViewLocationCacheItem"/>s for applicable _ViewStarts.</param>
|
||||
public ViewLocationCacheResult(
|
||||
string foundLocation,
|
||||
IEnumerable<string> searchedLocations)
|
||||
: this(searchedLocations)
|
||||
ViewLocationCacheItem view,
|
||||
IReadOnlyList<ViewLocationCacheItem> viewStarts)
|
||||
{
|
||||
if (foundLocation == null)
|
||||
if (viewStarts == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(foundLocation));
|
||||
throw new ArgumentNullException(nameof(viewStarts));
|
||||
}
|
||||
|
||||
ViewLocation = foundLocation;
|
||||
SearchedLocations = searchedLocations;
|
||||
IsFoundResult = true;
|
||||
ViewEntry = view;
|
||||
ViewStartEntries = viewStarts;
|
||||
Success = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -48,27 +44,26 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
|
||||
SearchedLocations = searchedLocations;
|
||||
ViewLocation = null;
|
||||
IsFoundResult = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="ViewLocationCacheResult"/> that represents a cache miss.
|
||||
/// <see cref="ViewLocationCacheItem"/> for the located view.
|
||||
/// </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>
|
||||
/// The location the view was found.
|
||||
/// <see cref="ViewLocationCacheItem"/>s for applicable _ViewStarts.
|
||||
/// </summary>
|
||||
/// <remarks>This is available if <see cref="IsFoundResult"/> is <c>true</c>.</remarks>
|
||||
public string ViewLocation { get; }
|
||||
/// <remarks><c>null</c> if <see cref="Success"/> is <c>false</c>.</remarks>
|
||||
public IReadOnlyList<ViewLocationCacheItem> ViewStartEntries { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The sequence of locations that were searched.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When <see cref="IsFoundResult"/> 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
|
||||
/// When <see cref="Success"/> is <c>true</c> this includes all paths that were search prior to finding
|
||||
/// a view at <see cref="ViewEntry"/>. When <see cref="Success"/> is <c>false</c>, this includes
|
||||
/// all search paths.
|
||||
/// </remarks>
|
||||
public IEnumerable<string> SearchedLocations { get; }
|
||||
|
|
@ -76,55 +71,6 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// <summary>
|
||||
/// Gets a value that indicates whether the view was successfully found.
|
||||
/// </summary>
|
||||
public bool IsFoundResult { 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;
|
||||
}
|
||||
public bool Success { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,10 +16,14 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// </summary>
|
||||
/// <param name="actionContext">The <see cref="Mvc.ActionContext"/> for the current executing action.</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>
|
||||
public ViewLocationExpanderContext(
|
||||
ActionContext actionContext,
|
||||
string viewName,
|
||||
string controllerName,
|
||||
string areaName,
|
||||
bool isPartial)
|
||||
{
|
||||
if (actionContext == null)
|
||||
|
|
@ -34,6 +38,8 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
ActionContext = actionContext;
|
||||
ViewName = viewName;
|
||||
ControllerName = controllerName;
|
||||
AreaName = areaName;
|
||||
IsPartial = isPartial;
|
||||
}
|
||||
|
||||
|
|
@ -47,6 +53,16 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// </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>
|
||||
/// Gets a value that determines if a partial view is being discovered.
|
||||
/// </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
|
||||
var compilationFailure = new CompilationFailure("test", Enumerable.Empty<Microsoft.Extensions.PlatformAbstractions.DiagnosticMessage>());
|
||||
var failures = new[] { compilationFailure };
|
||||
var result = CompilationResult.Failed(failures);
|
||||
var result = new CompilationResult(failures);
|
||||
|
||||
// Act and Assert
|
||||
Assert.Null(result.CompiledType);
|
||||
|
|
|
|||
|
|
@ -36,8 +36,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
var result = cache.GetOrAdd("/some/path", ThrowsIfCalled);
|
||||
|
||||
// Assert
|
||||
Assert.Same(CompilerCacheResult.FileNotFound, result);
|
||||
Assert.Null(result.CompilationResult);
|
||||
Assert.False(result.Success);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -48,18 +47,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
fileProvider.AddFile(ViewPath, "some content");
|
||||
var cache = new CompilerCache(fileProvider);
|
||||
var type = typeof(TestView);
|
||||
var expected = UncachedCompilationResult.Successful(type, "hello world");
|
||||
var expected = new CompilationResult(type);
|
||||
|
||||
// Act
|
||||
var result = cache.GetOrAdd(ViewPath, _ => expected);
|
||||
|
||||
// Assert
|
||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result);
|
||||
var actual = Assert.IsType<UncachedCompilationResult>(result.CompilationResult);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Same(expected, actual);
|
||||
Assert.Equal("hello world", actual.CompiledContent);
|
||||
Assert.Same(type, actual.CompiledType);
|
||||
Assert.True(result.Success);
|
||||
Assert.Same(type, result.CompilationResult.CompiledType);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -75,15 +70,13 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
fileProvider.AddFile(viewPath, "some content");
|
||||
var cache = new CompilerCache(fileProvider);
|
||||
var type = typeof(TestView);
|
||||
var expected = UncachedCompilationResult.Successful(type, "hello world");
|
||||
var expected = new CompilationResult(type);
|
||||
|
||||
// Act - 1
|
||||
var result1 = cache.GetOrAdd(@"Areas\Finances\Views\Home\Index.cshtml", _ => expected);
|
||||
|
||||
// Assert - 1
|
||||
var compilationResult = Assert.IsType<UncachedCompilationResult>(result1.CompilationResult);
|
||||
Assert.Same(expected, compilationResult);
|
||||
Assert.Same(type, compilationResult.CompiledType);
|
||||
Assert.Same(type, result1.CompilationResult.CompiledType);
|
||||
|
||||
// Act - 2
|
||||
var result2 = cache.GetOrAdd(relativePath, ThrowsIfCalled);
|
||||
|
|
@ -93,21 +86,21 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void GetOrAdd_ReturnsFileNotFoundIfFileWasDeleted()
|
||||
public void GetOrAdd_ReturnsFailedCompilationResult_IfFileWasRemovedFromFileSystem()
|
||||
{
|
||||
// Arrange
|
||||
var fileProvider = new TestFileProvider();
|
||||
fileProvider.AddFile(ViewPath, "some content");
|
||||
var cache = new CompilerCache(fileProvider);
|
||||
var type = typeof(TestView);
|
||||
var expected = UncachedCompilationResult.Successful(type, "hello world");
|
||||
var expected = new CompilationResult(type);
|
||||
|
||||
// Act 1
|
||||
var result1 = cache.GetOrAdd(ViewPath, _ => expected);
|
||||
|
||||
// Assert 1
|
||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result1);
|
||||
Assert.Same(expected, result1.CompilationResult);
|
||||
Assert.True(result1.Success);
|
||||
Assert.Same(expected.CompiledType, result1.CompilationResult.CompiledType);
|
||||
|
||||
// Act 2
|
||||
// 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);
|
||||
|
||||
// Assert 2
|
||||
Assert.Same(CompilerCacheResult.FileNotFound, result2);
|
||||
Assert.Null(result2.CompilationResult);
|
||||
Assert.False(result2.Success);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -127,22 +119,22 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
var fileProvider = new TestFileProvider();
|
||||
fileProvider.AddFile(ViewPath, "some content");
|
||||
var cache = new CompilerCache(fileProvider);
|
||||
var expected1 = UncachedCompilationResult.Successful(typeof(TestView), "hello world");
|
||||
var expected2 = UncachedCompilationResult.Successful(typeof(DifferentView), "different content");
|
||||
var expected1 = new CompilationResult(typeof(TestView));
|
||||
var expected2 = new CompilationResult(typeof(DifferentView));
|
||||
|
||||
// Act 1
|
||||
var result1 = cache.GetOrAdd(ViewPath, _ => expected1);
|
||||
|
||||
// Assert 1
|
||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result1);
|
||||
Assert.Same(expected1, result1.CompilationResult);
|
||||
Assert.True(result1.Success);
|
||||
Assert.Same(typeof(TestView), result1.CompilationResult.CompiledType);
|
||||
|
||||
// Act 2
|
||||
// Verify we're getting cached results.
|
||||
var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
|
||||
|
||||
// Assert 2
|
||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result2);
|
||||
Assert.True(result2.Success);
|
||||
Assert.Same(expected1.CompiledType, result2.CompilationResult.CompiledType);
|
||||
|
||||
// Act 3
|
||||
|
|
@ -150,8 +142,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
var result3 = cache.GetOrAdd(ViewPath, _ => expected2);
|
||||
|
||||
// Assert 3
|
||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result3);
|
||||
Assert.Same(expected2, result3.CompilationResult);
|
||||
Assert.True(result3.Success);
|
||||
Assert.Same(expected2.CompiledType, result3.CompilationResult.CompiledType);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -162,22 +154,22 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
var fileProvider = new TestFileProvider();
|
||||
fileProvider.AddFile(ViewPath, "some content");
|
||||
var cache = new CompilerCache(fileProvider);
|
||||
var expected1 = UncachedCompilationResult.Successful(typeof(TestView), "hello world");
|
||||
var expected2 = UncachedCompilationResult.Successful(typeof(DifferentView), "different content");
|
||||
var expected1 = new CompilationResult(typeof(TestView));
|
||||
var expected2 = new CompilationResult(typeof(DifferentView));
|
||||
|
||||
// Act 1
|
||||
var result1 = cache.GetOrAdd(ViewPath, _ => expected1);
|
||||
|
||||
// Assert 1
|
||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result1);
|
||||
Assert.Same(expected1, result1.CompilationResult);
|
||||
Assert.True(result1.Success);
|
||||
Assert.Same(expected1.CompiledType, result1.CompilationResult.CompiledType);
|
||||
|
||||
// Act 2
|
||||
// Verify we're getting cached results.
|
||||
var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
|
||||
|
||||
// Assert 2
|
||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result2);
|
||||
Assert.True(result2.Success);
|
||||
Assert.Same(expected1.CompiledType, result2.CompilationResult.CompiledType);
|
||||
|
||||
// Act 3
|
||||
|
|
@ -185,8 +177,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
var result3 = cache.GetOrAdd(ViewPath, _ => expected2);
|
||||
|
||||
// Assert 2
|
||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result3);
|
||||
Assert.Same(expected2, result3.CompilationResult);
|
||||
Assert.True(result3.Success);
|
||||
Assert.Same(expected2.CompiledType, result3.CompilationResult.CompiledType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -198,21 +190,20 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
fileProvider.AddFile(ViewPath, "some content");
|
||||
var cache = new CompilerCache(fileProvider);
|
||||
var type = typeof(TestView);
|
||||
var expected = UncachedCompilationResult.Successful(type, "hello world");
|
||||
var expected = new CompilationResult(type);
|
||||
|
||||
// Act 1
|
||||
var result1 = cache.GetOrAdd(ViewPath, _ => expected);
|
||||
|
||||
// Assert 1
|
||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result1);
|
||||
Assert.Same(expected, result1.CompilationResult);
|
||||
Assert.True(result1.Success);
|
||||
Assert.Same(type, result1.CompilationResult.CompiledType);
|
||||
|
||||
// Act 2
|
||||
var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
|
||||
|
||||
// Assert 2
|
||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result2);
|
||||
Assert.IsType<CompilationResult>(result2.CompilationResult);
|
||||
Assert.True(result2.Success);
|
||||
Assert.Same(type, result2.CompilationResult.CompiledType);
|
||||
mockFileProvider.Verify(v => v.GetFileInfo(ViewPath), Times.Once());
|
||||
}
|
||||
|
|
@ -228,7 +219,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
var result = cache.GetOrAdd(PrecompiledViewsPath, ThrowsIfCalled);
|
||||
|
||||
// Assert
|
||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result);
|
||||
Assert.True(result.Success);
|
||||
Assert.Same(typeof(PreCompile), result.CompilationResult.CompiledType);
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +236,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
var result = cache.GetOrAdd(PrecompiledViewsPath, ThrowsIfCalled);
|
||||
|
||||
// Assert
|
||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result);
|
||||
Assert.True(result.Success);
|
||||
Assert.Same(typeof(PreCompile), result.CompilationResult.CompiledType);
|
||||
}
|
||||
|
||||
|
|
@ -263,7 +254,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
var result = cache.GetOrAdd(PrecompiledViewsPath, ThrowsIfCalled);
|
||||
|
||||
// Assert
|
||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result);
|
||||
Assert.True(result.Success);
|
||||
Assert.Same(typeof(PreCompile), result.CompilationResult.CompiledType);
|
||||
}
|
||||
|
||||
|
|
@ -274,26 +265,26 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
var fileProvider = new TestFileProvider();
|
||||
fileProvider.AddFile(ViewPath, "some content");
|
||||
var cache = new CompilerCache(fileProvider, _precompiledViews);
|
||||
var expected = CompilationResult.Successful(typeof(TestView));
|
||||
var expected = new CompilationResult(typeof(TestView));
|
||||
|
||||
// Act 1
|
||||
var result1 = cache.GetOrAdd(ViewPath, _ => expected);
|
||||
|
||||
// Assert 1
|
||||
Assert.Same(expected, result1.CompilationResult);
|
||||
Assert.Same(typeof(TestView), result1.CompilationResult.CompiledType);
|
||||
|
||||
// Act 2
|
||||
var result2 = cache.GetOrAdd(ViewPath, ThrowsIfCalled);
|
||||
|
||||
// Assert 2
|
||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result2);
|
||||
Assert.True(result2.Success);
|
||||
Assert.Same(typeof(TestView), result2.CompilationResult.CompiledType);
|
||||
|
||||
// Act 3
|
||||
var result3 = cache.GetOrAdd(PrecompiledViewsPath, ThrowsIfCalled);
|
||||
|
||||
// Assert 3
|
||||
Assert.NotSame(CompilerCacheResult.FileNotFound, result2);
|
||||
Assert.True(result2.Success);
|
||||
Assert.Same(typeof(PreCompile), result3.CompilationResult.CompiledType);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
|
||||
var compiler = new Mock<ICompilationService>();
|
||||
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());
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
.Returns(Stream.Null);
|
||||
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>();
|
||||
compiler.Setup(c => c.Compile(relativeFileInfo, code))
|
||||
.Returns(compilationResult)
|
||||
|
|
@ -117,7 +117,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
var result = razorService.Compile(relativeFileInfo);
|
||||
|
||||
// Assert
|
||||
Assert.Same(compilationResult, result);
|
||||
Assert.Same(compilationResult.CompiledType, result.CompiledType);
|
||||
compiler.Verify();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|||
public class RoslynCompilationServiceTest
|
||||
{
|
||||
[Fact]
|
||||
public void Compile_ReturnsUncachedCompilationResultWithCompiledContent()
|
||||
public void Compile_ReturnsCompilationResult()
|
||||
{
|
||||
// Arrange
|
||||
var content = @"
|
||||
|
|
@ -29,29 +29,31 @@ public class MyTestType {}";
|
|||
var libraryExporter = GetLibraryExporter();
|
||||
|
||||
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
||||
compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName,
|
||||
applicationEnvironment.RuntimeFramework,
|
||||
applicationEnvironment.Configuration))
|
||||
.Returns(new CompilerOptions());
|
||||
compilerOptionsProvider
|
||||
.Setup(p => p.GetCompilerOptions(
|
||||
applicationEnvironment.ApplicationName,
|
||||
applicationEnvironment.RuntimeFramework,
|
||||
applicationEnvironment.Configuration))
|
||||
.Returns(new CompilerOptions());
|
||||
var mvcRazorHost = new Mock<IMvcRazorHost>();
|
||||
mvcRazorHost.SetupGet(m => m.MainClassNamePrefix)
|
||||
.Returns(string.Empty);
|
||||
|
||||
var compilationService = new RoslynCompilationService(applicationEnvironment,
|
||||
libraryExporter,
|
||||
compilerOptionsProvider.Object,
|
||||
mvcRazorHost.Object,
|
||||
GetOptions());
|
||||
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
|
||||
var compilationService = new RoslynCompilationService(
|
||||
applicationEnvironment,
|
||||
libraryExporter,
|
||||
compilerOptionsProvider.Object,
|
||||
mvcRazorHost.Object,
|
||||
GetOptions());
|
||||
var relativeFileInfo = new RelativeFileInfo(
|
||||
new TestFileInfo { PhysicalPath = "SomePath" },
|
||||
"some-relative-path");
|
||||
|
||||
// Act
|
||||
var result = compilationService.Compile(relativeFileInfo, content);
|
||||
|
||||
// Assert
|
||||
var uncachedResult = Assert.IsType<UncachedCompilationResult>(result);
|
||||
Assert.Equal("MyTestType", result.CompiledType.Name);
|
||||
Assert.Equal(content, uncachedResult.CompiledContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -67,19 +69,22 @@ this should fail";
|
|||
var libraryExporter = GetLibraryExporter();
|
||||
|
||||
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
||||
compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName,
|
||||
applicationEnvironment.RuntimeFramework,
|
||||
applicationEnvironment.Configuration))
|
||||
.Returns(new CompilerOptions());
|
||||
compilerOptionsProvider
|
||||
.Setup(p => p.GetCompilerOptions(
|
||||
applicationEnvironment.ApplicationName,
|
||||
applicationEnvironment.RuntimeFramework,
|
||||
applicationEnvironment.Configuration))
|
||||
.Returns(new CompilerOptions());
|
||||
var mvcRazorHost = Mock.Of<IMvcRazorHost>();
|
||||
var fileProvider = new TestFileProvider();
|
||||
var fileInfo = fileProvider.AddFile(viewPath, fileContent);
|
||||
|
||||
var compilationService = new RoslynCompilationService(applicationEnvironment,
|
||||
libraryExporter,
|
||||
compilerOptionsProvider.Object,
|
||||
mvcRazorHost,
|
||||
GetOptions(fileProvider));
|
||||
var compilationService = new RoslynCompilationService(
|
||||
applicationEnvironment,
|
||||
libraryExporter,
|
||||
compilerOptionsProvider.Object,
|
||||
mvcRazorHost,
|
||||
GetOptions(fileProvider));
|
||||
var relativeFileInfo = new RelativeFileInfo(fileInfo, "some-relative-path");
|
||||
|
||||
// Act
|
||||
|
|
@ -103,18 +108,22 @@ this should fail";
|
|||
var libraryExporter = GetLibraryExporter();
|
||||
|
||||
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
||||
compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName,
|
||||
applicationEnvironment.RuntimeFramework,
|
||||
applicationEnvironment.Configuration))
|
||||
.Returns(new CompilerOptions());
|
||||
compilerOptionsProvider
|
||||
.Setup(p => p.GetCompilerOptions(
|
||||
applicationEnvironment.ApplicationName,
|
||||
applicationEnvironment.RuntimeFramework,
|
||||
applicationEnvironment.Configuration))
|
||||
.Returns(new CompilerOptions());
|
||||
var mvcRazorHost = Mock.Of<IMvcRazorHost>();
|
||||
|
||||
var compilationService = new RoslynCompilationService(applicationEnvironment,
|
||||
libraryExporter,
|
||||
compilerOptionsProvider.Object,
|
||||
mvcRazorHost,
|
||||
GetOptions());
|
||||
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { Content = fileContent },
|
||||
var compilationService = new RoslynCompilationService(
|
||||
applicationEnvironment,
|
||||
libraryExporter,
|
||||
compilerOptionsProvider.Object,
|
||||
mvcRazorHost,
|
||||
GetOptions());
|
||||
var relativeFileInfo = new RelativeFileInfo(
|
||||
new TestFileInfo { Content = fileContent },
|
||||
"some-relative-path");
|
||||
|
||||
// Act
|
||||
|
|
@ -141,10 +150,12 @@ this should fail";
|
|||
var libraryExporter = GetLibraryExporter();
|
||||
|
||||
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
||||
compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName,
|
||||
applicationEnvironment.RuntimeFramework,
|
||||
applicationEnvironment.Configuration))
|
||||
.Returns(new CompilerOptions());
|
||||
compilerOptionsProvider
|
||||
.Setup(p => p.GetCompilerOptions(
|
||||
applicationEnvironment.ApplicationName,
|
||||
applicationEnvironment.RuntimeFramework,
|
||||
applicationEnvironment.Configuration))
|
||||
.Returns(new CompilerOptions());
|
||||
var mvcRazorHost = Mock.Of<IMvcRazorHost>();
|
||||
|
||||
var mockFileInfo = new Mock<IFileInfo>();
|
||||
|
|
@ -153,11 +164,12 @@ this should fail";
|
|||
var fileProvider = new TestFileProvider();
|
||||
fileProvider.AddFile(path, mockFileInfo.Object);
|
||||
|
||||
var compilationService = new RoslynCompilationService(applicationEnvironment,
|
||||
libraryExporter,
|
||||
compilerOptionsProvider.Object,
|
||||
mvcRazorHost,
|
||||
GetOptions(fileProvider));
|
||||
var compilationService = new RoslynCompilationService(
|
||||
applicationEnvironment,
|
||||
libraryExporter,
|
||||
compilerOptionsProvider.Object,
|
||||
mvcRazorHost,
|
||||
GetOptions(fileProvider));
|
||||
|
||||
var relativeFileInfo = new RelativeFileInfo(mockFileInfo.Object, path);
|
||||
|
||||
|
|
@ -187,20 +199,24 @@ public class MyNonCustomDefinedClass {}
|
|||
var libraryExporter = GetLibraryExporter();
|
||||
|
||||
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
||||
compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName,
|
||||
applicationEnvironment.RuntimeFramework,
|
||||
applicationEnvironment.Configuration))
|
||||
.Returns(new CompilerOptions { Defines = new[] { "MY_CUSTOM_DEFINE" } });
|
||||
compilerOptionsProvider
|
||||
.Setup(p => p.GetCompilerOptions(
|
||||
applicationEnvironment.ApplicationName,
|
||||
applicationEnvironment.RuntimeFramework,
|
||||
applicationEnvironment.Configuration))
|
||||
.Returns(new CompilerOptions { Defines = new[] { "MY_CUSTOM_DEFINE" } });
|
||||
var mvcRazorHost = new Mock<IMvcRazorHost>();
|
||||
mvcRazorHost.SetupGet(m => m.MainClassNamePrefix)
|
||||
.Returns("My");
|
||||
|
||||
var compilationService = new RoslynCompilationService(applicationEnvironment,
|
||||
libraryExporter,
|
||||
compilerOptionsProvider.Object,
|
||||
mvcRazorHost.Object,
|
||||
GetOptions());
|
||||
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
|
||||
var compilationService = new RoslynCompilationService(
|
||||
applicationEnvironment,
|
||||
libraryExporter,
|
||||
compilerOptionsProvider.Object,
|
||||
mvcRazorHost.Object,
|
||||
GetOptions());
|
||||
var relativeFileInfo = new RelativeFileInfo(
|
||||
new TestFileInfo { PhysicalPath = "SomePath" },
|
||||
"some-relative-path");
|
||||
|
||||
// Act
|
||||
|
|
@ -222,21 +238,25 @@ public class NotRazorPrefixType {}";
|
|||
var libraryExporter = GetLibraryExporter();
|
||||
|
||||
var compilerOptionsProvider = new Mock<ICompilerOptionsProvider>();
|
||||
compilerOptionsProvider.Setup(p => p.GetCompilerOptions(applicationEnvironment.ApplicationName,
|
||||
applicationEnvironment.RuntimeFramework,
|
||||
applicationEnvironment.Configuration))
|
||||
.Returns(new CompilerOptions());
|
||||
compilerOptionsProvider
|
||||
.Setup(p => p.GetCompilerOptions(
|
||||
applicationEnvironment.ApplicationName,
|
||||
applicationEnvironment.RuntimeFramework,
|
||||
applicationEnvironment.Configuration))
|
||||
.Returns(new CompilerOptions());
|
||||
var mvcRazorHost = new Mock<IMvcRazorHost>();
|
||||
mvcRazorHost.SetupGet(m => m.MainClassNamePrefix)
|
||||
.Returns("RazorPrefix");
|
||||
|
||||
var compilationService = new RoslynCompilationService(applicationEnvironment,
|
||||
libraryExporter,
|
||||
compilerOptionsProvider.Object,
|
||||
mvcRazorHost.Object,
|
||||
GetOptions());
|
||||
var compilationService = new RoslynCompilationService(
|
||||
applicationEnvironment,
|
||||
libraryExporter,
|
||||
compilerOptionsProvider.Object,
|
||||
mvcRazorHost.Object,
|
||||
GetOptions());
|
||||
|
||||
var relativeFileInfo = new RelativeFileInfo(new TestFileInfo { PhysicalPath = "SomePath" },
|
||||
var relativeFileInfo = new RelativeFileInfo(
|
||||
new TestFileInfo { PhysicalPath = "SomePath" },
|
||||
"some-relative-path");
|
||||
|
||||
// Act
|
||||
|
|
@ -356,27 +376,33 @@ public class NotRazorPrefixType {}";
|
|||
private static ILibraryExporter GetLibraryExporter()
|
||||
{
|
||||
var fileReference = new Mock<IMetadataFileReference>();
|
||||
fileReference.SetupGet(f => f.Path)
|
||||
.Returns(typeof(string).Assembly.Location);
|
||||
fileReference
|
||||
.SetupGet(f => f.Path)
|
||||
.Returns(typeof(string).Assembly.Location);
|
||||
var libraryExport = new LibraryExport(fileReference.Object);
|
||||
|
||||
var libraryExporter = new Mock<ILibraryExporter>();
|
||||
libraryExporter.Setup(l => l.GetAllExports(It.IsAny<string>()))
|
||||
.Returns(libraryExport);
|
||||
libraryExporter
|
||||
.Setup(l => l.GetAllExports(It.IsAny<string>()))
|
||||
.Returns(libraryExport);
|
||||
return libraryExporter.Object;
|
||||
}
|
||||
|
||||
private IApplicationEnvironment GetApplicationEnvironment()
|
||||
{
|
||||
var applicationEnvironment = new Mock<IApplicationEnvironment>();
|
||||
applicationEnvironment.SetupGet(a => a.ApplicationName)
|
||||
.Returns("MyApp");
|
||||
applicationEnvironment.SetupGet(a => a.RuntimeFramework)
|
||||
.Returns(new FrameworkName("ASPNET", new Version(5, 0)));
|
||||
applicationEnvironment.SetupGet(a => a.Configuration)
|
||||
.Returns("Debug");
|
||||
applicationEnvironment.SetupGet(a => a.ApplicationBasePath)
|
||||
.Returns("MyBasePath");
|
||||
applicationEnvironment
|
||||
.SetupGet(a => a.ApplicationName)
|
||||
.Returns("MyApp");
|
||||
applicationEnvironment
|
||||
.SetupGet(a => a.RuntimeFramework)
|
||||
.Returns(new FrameworkName("ASPNET", new Version(5, 0)));
|
||||
applicationEnvironment
|
||||
.SetupGet(a => a.Configuration)
|
||||
.Returns("Debug");
|
||||
applicationEnvironment
|
||||
.SetupGet(a => a.ApplicationBasePath)
|
||||
.Returns("MyBasePath");
|
||||
|
||||
return applicationEnvironment.Object;
|
||||
}
|
||||
|
|
@ -388,7 +414,8 @@ public class NotRazorPrefixType {}";
|
|||
FileProvider = fileProvider ?? new TestFileProvider()
|
||||
};
|
||||
var options = new Mock<IOptions<RazorViewEngineOptions>>();
|
||||
options.SetupGet(o => o.Value)
|
||||
options
|
||||
.SetupGet(o => o.Value)
|
||||
.Returns(razorViewEngineOptions);
|
||||
|
||||
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)
|
||||
{
|
||||
// Arrange
|
||||
var viewLocationExpanderContext = new ViewLocationExpanderContext(new ActionContext(),"testView", false);
|
||||
var viewLocationExpanderContext = new ViewLocationExpanderContext(
|
||||
new ActionContext(),
|
||||
"testView",
|
||||
"test-controller",
|
||||
"",
|
||||
false);
|
||||
var languageViewLocationExpander = new LanguageViewLocationExpander(format);
|
||||
viewLocationExpanderContext.Values = new Dictionary<string, string>();
|
||||
viewLocationExpanderContext.Values["language"] = "en-GB";
|
||||
|
|
@ -150,7 +155,12 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
public void ExpandViewLocations_NullContextValue(IEnumerable<string> viewLocations)
|
||||
{
|
||||
// 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();
|
||||
viewLocationExpanderContext.Values = new Dictionary<string, string>();
|
||||
|
||||
|
|
@ -168,7 +178,12 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
public void ExpandViewLocations_IncorrectLocaleContextValue(IEnumerable<string> viewLocations)
|
||||
{
|
||||
// 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();
|
||||
viewLocationExpanderContext.Values = new Dictionary<string, string>();
|
||||
viewLocationExpanderContext.Values["language"] = "!-invalid-culture-!";
|
||||
|
|
|
|||
|
|
@ -1,16 +1,19 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.AspNet.Mvc.Abstractions;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.AspNet.Mvc.Routing;
|
||||
using Microsoft.AspNet.Mvc.ViewEngines;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.OptionsModel;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Extensions.WebEncoders.Testing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -116,18 +119,24 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
public void FindPartialView_ReturnsRazorView_IfLookupWasSuccessful()
|
||||
{
|
||||
// Arrange
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
var viewFactory = new Mock<IRazorViewFactory>();
|
||||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
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>()))
|
||||
.Returns(Mock.Of<IRazorPage>());
|
||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorViewEngine>(), It.IsAny<IRazorPage>(), true))
|
||||
.Returns(view)
|
||||
.Verifiable();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Views/bar/test-view.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]));
|
||||
|
||||
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);
|
||||
|
||||
// Act
|
||||
|
|
@ -135,9 +144,52 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
|
||||
// Assert
|
||||
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);
|
||||
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]
|
||||
|
|
@ -150,8 +202,9 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
var context = GetActionContext(_controllerTestContext);
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.ThrowsArgumentNullOrEmpty(() => viewEngine.FindPartialView(context, partialViewName),
|
||||
"partialViewName");
|
||||
ExceptionAssert.ThrowsArgumentNullOrEmpty(
|
||||
() => viewEngine.FindPartialView(context, partialViewName),
|
||||
"partialViewName");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -266,18 +319,24 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
public void FindView_ReturnsRazorView_IfLookupWasSuccessful()
|
||||
{
|
||||
// Arrange
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
var viewFactory = new Mock<IRazorViewFactory>();
|
||||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
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>()))
|
||||
.Returns(Mock.Of<IRazorPage>());
|
||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorViewEngine>(), It.IsAny<IRazorPage>(), false))
|
||||
.Returns(view)
|
||||
.Verifiable();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Views/bar/test-view.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]));
|
||||
|
||||
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);
|
||||
|
||||
// Act
|
||||
|
|
@ -285,63 +344,66 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
|
||||
// Assert
|
||||
Assert.True(result.Success);
|
||||
Assert.Same(view, result.View);
|
||||
var view = Assert.IsType<RazorView>(result.View);
|
||||
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]
|
||||
public void FindView_UsesViewLocationFormat_IfRouteDoesNotContainArea()
|
||||
{
|
||||
// Arrange
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
var viewFactory = new Mock<IRazorViewFactory>();
|
||||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
|
||||
var page = Mock.Of<IRazorPage>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateInstance("fake-path1/bar/test-view.rzr"))
|
||||
.Returns(page)
|
||||
.Setup(p => p.CreateFactory("fake-path1/bar/test-view.rzr"))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Verifiable();
|
||||
var viewEngine = new OverloadedLocationViewEngine(
|
||||
var viewEngine = new TestableRazorViewEngine(
|
||||
pageFactory.Object,
|
||||
viewFactory.Object,
|
||||
GetOptionsAccessor(),
|
||||
GetViewLocationCache());
|
||||
GetOptionsAccessor());
|
||||
viewEngine.SetLocationFormats(
|
||||
new[] { "fake-path1/{1}/{0}.rzr" },
|
||||
new[] { "fake-area-path/{2}/{1}/{0}.rzr" });
|
||||
var context = GetActionContext(_controllerTestContext);
|
||||
viewFactory.Setup(v => v.GetView(viewEngine, page, false))
|
||||
.Returns(Mock.Of<IView>());
|
||||
|
||||
// Act
|
||||
var result = viewEngine.FindView(context, "test-view");
|
||||
|
||||
// Assert
|
||||
pageFactory.Verify();
|
||||
var view = Assert.IsType<RazorView>(result.View);
|
||||
Assert.Same(page, view.RazorPage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindView_UsesAreaViewLocationFormat_IfRouteContainsArea()
|
||||
{
|
||||
// Arrange
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
var viewFactory = new Mock<IRazorViewFactory>();
|
||||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
var page = Mock.Of<IRazorPage>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateInstance("fake-area-path/foo/bar/test-view2.rzr"))
|
||||
.Returns(page)
|
||||
.Setup(p => p.CreateFactory("fake-area-path/foo/bar/test-view2.rzr"))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Verifiable();
|
||||
var viewEngine = new OverloadedLocationViewEngine(
|
||||
var viewEngine = new TestableRazorViewEngine(
|
||||
pageFactory.Object,
|
||||
viewFactory.Object,
|
||||
GetOptionsAccessor(),
|
||||
GetViewLocationCache());
|
||||
GetOptionsAccessor());
|
||||
viewEngine.SetLocationFormats(
|
||||
new[] { "fake-path1/{1}/{0}.rzr" },
|
||||
new[] { "fake-area-path/{2}/{1}/{0}.rzr" });
|
||||
var context = GetActionContext(_areaTestContext);
|
||||
viewFactory.Setup(v => v.GetView(viewEngine, page, false))
|
||||
.Returns(Mock.Of<IView>());
|
||||
|
||||
// Act
|
||||
var result = viewEngine.FindView(context, "test-view2");
|
||||
|
||||
// Assert
|
||||
pageFactory.Verify();
|
||||
var view = Assert.IsType<RazorView>(result.View);
|
||||
Assert.Same(page, view.RazorPage);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -351,46 +413,49 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
IEnumerable<string> expectedSeeds)
|
||||
{
|
||||
// Arrange
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("test-string/bar.cshtml"))
|
||||
.Returns(Mock.Of<IRazorPage>())
|
||||
.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 pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("test-string/bar.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => Mock.Of<IRazorPage>(), new IChangeToken[0]))
|
||||
.Verifiable();
|
||||
|
||||
var expander1Result = new[] { "some-seed" };
|
||||
var expander1 = new Mock<IViewLocationExpander>();
|
||||
expander1.Setup(e => e.PopulateValues(It.IsAny<ViewLocationExpanderContext>()))
|
||||
.Callback((ViewLocationExpanderContext c) =>
|
||||
{
|
||||
Assert.NotNull(c.ActionContext);
|
||||
c.Values["expander-key"] = expander1.ToString();
|
||||
})
|
||||
.Verifiable();
|
||||
expander1.Setup(e => e.ExpandViewLocations(It.IsAny<ViewLocationExpanderContext>(),
|
||||
It.IsAny<IEnumerable<string>>()))
|
||||
.Callback((ViewLocationExpanderContext c, IEnumerable<string> seeds) =>
|
||||
{
|
||||
Assert.NotNull(c.ActionContext);
|
||||
Assert.Equal(expectedSeeds, seeds);
|
||||
})
|
||||
.Returns(expander1Result)
|
||||
.Verifiable();
|
||||
expander1
|
||||
.Setup(e => e.PopulateValues(It.IsAny<ViewLocationExpanderContext>()))
|
||||
.Callback((ViewLocationExpanderContext c) =>
|
||||
{
|
||||
Assert.NotNull(c.ActionContext);
|
||||
c.Values["expander-key"] = expander1.ToString();
|
||||
})
|
||||
.Verifiable();
|
||||
expander1
|
||||
.Setup(e => e.ExpandViewLocations(
|
||||
It.IsAny<ViewLocationExpanderContext>(),
|
||||
It.IsAny<IEnumerable<string>>()))
|
||||
.Callback((ViewLocationExpanderContext c, IEnumerable<string> seeds) =>
|
||||
{
|
||||
Assert.NotNull(c.ActionContext);
|
||||
Assert.Equal(expectedSeeds, seeds);
|
||||
})
|
||||
.Returns(expander1Result)
|
||||
.Verifiable();
|
||||
|
||||
var expander2 = new Mock<IViewLocationExpander>();
|
||||
expander2.Setup(e => e.ExpandViewLocations(It.IsAny<ViewLocationExpanderContext>(),
|
||||
It.IsAny<IEnumerable<string>>()))
|
||||
.Callback((ViewLocationExpanderContext c, IEnumerable<string> seeds) =>
|
||||
{
|
||||
Assert.Equal(expander1Result, seeds);
|
||||
})
|
||||
.Returns(new[] { "test-string/{1}.cshtml" })
|
||||
.Verifiable();
|
||||
expander2
|
||||
.Setup(e => e.ExpandViewLocations(
|
||||
It.IsAny<ViewLocationExpanderContext>(),
|
||||
It.IsAny<IEnumerable<string>>()))
|
||||
.Callback((ViewLocationExpanderContext c, IEnumerable<string> seeds) =>
|
||||
{
|
||||
Assert.Equal(expander1Result, seeds);
|
||||
})
|
||||
.Returns(new[] { "test-string/{1}.cshtml" })
|
||||
.Verifiable();
|
||||
|
||||
var viewEngine = CreateViewEngine(pageFactory.Object, viewFactory.Object,
|
||||
new[] { expander1.Object, expander2.Object });
|
||||
var viewEngine = CreateViewEngine(
|
||||
pageFactory.Object,
|
||||
new[] { expander1.Object, expander2.Object });
|
||||
var context = GetActionContext(routeValues);
|
||||
|
||||
// Act
|
||||
|
|
@ -408,130 +473,152 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
public void FindView_CachesValuesIfViewWasFound()
|
||||
{
|
||||
// Arrange
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("/Views/bar/baz.cshtml"))
|
||||
.Verifiable();
|
||||
pageFactory.Setup(p => p.CreateInstance("/Views/Shared/baz.cshtml"))
|
||||
.Returns(Mock.Of<IRazorPage>())
|
||||
.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);
|
||||
})
|
||||
var page = Mock.Of<IRazorPage>();
|
||||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Views/bar/baz.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(new IChangeToken[0]))
|
||||
.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);
|
||||
|
||||
// Act
|
||||
var result = viewEngine.FindView(context, "baz");
|
||||
// Act 1
|
||||
var result1 = viewEngine.FindView(context, "baz");
|
||||
|
||||
// Assert
|
||||
Assert.True(result.Success);
|
||||
// Assert 1
|
||||
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();
|
||||
cacheMock.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindView_UsesCachedValueIfViewWasFound()
|
||||
public void FindView_InvokesPageFactoryIfChangeTokenExpired()
|
||||
{
|
||||
// Arrange
|
||||
var pageFactory = new Mock<IRazorPageFactory>(MockBehavior.Strict);
|
||||
pageFactory.Setup(p => p.CreateInstance("some-view-location"))
|
||||
.Returns(Mock.Of<IRazorPage>())
|
||||
.Verifiable();
|
||||
var page1 = Mock.Of<IRazorPage>();
|
||||
var page2 = Mock.Of<IRazorPage>();
|
||||
var sequence = new MockSequence();
|
||||
var cancellationTokenSource = new CancellationTokenSource();
|
||||
var changeToken = new CancellationChangeToken(cancellationTokenSource.Token);
|
||||
|
||||
var viewFactory = new Mock<IRazorViewFactory>();
|
||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorViewEngine>(), It.IsAny<IRazorPage>(), false))
|
||||
.Returns(Mock.Of<IView>());
|
||||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.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);
|
||||
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 viewEngine = CreateViewEngine(pageFactory.Object);
|
||||
var context = GetActionContext(_controllerTestContext);
|
||||
|
||||
// Act
|
||||
var result = viewEngine.FindView(context, "baz");
|
||||
// Act 1
|
||||
var result1 = viewEngine.FindView(context, "baz");
|
||||
|
||||
// Assert
|
||||
Assert.True(result.Success);
|
||||
// Assert 1
|
||||
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();
|
||||
cacheMock.Verify();
|
||||
expander.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindView_LooksForViewsIfCachedViewDoesNotExist()
|
||||
public void FindView_InvokesPageFactoryIfViewStartExpirationTokensHaveExpired()
|
||||
{
|
||||
// Arrange
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("expired-location"))
|
||||
.Returns((IRazorPage)null)
|
||||
.Verifiable();
|
||||
pageFactory.Setup(p => p.CreateInstance("some-view-location"))
|
||||
.Returns(Mock.Of<IRazorPage>())
|
||||
.Verifiable();
|
||||
var page1 = Mock.Of<IRazorPage>();
|
||||
var page2 = Mock.Of<IRazorPage>();
|
||||
var viewStart = Mock.Of<IRazorPage>();
|
||||
var sequence = new MockSequence();
|
||||
var cancellationTokenSource = new CancellationTokenSource();
|
||||
var changeToken = new CancellationChangeToken(cancellationTokenSource.Token);
|
||||
|
||||
var viewFactory = new Mock<IRazorViewFactory>();
|
||||
viewFactory.Setup(p => p.GetView(It.IsAny<IRazorViewEngine>(), It.IsAny<IRazorPage>(), false))
|
||||
.Returns(Mock.Of<IView>());
|
||||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.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>();
|
||||
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 viewEngine = CreateViewEngine(pageFactory.Object);
|
||||
var context = GetActionContext(_controllerTestContext);
|
||||
|
||||
// Act
|
||||
var result = viewEngine.FindView(context, "baz");
|
||||
// Act 1
|
||||
var result1 = viewEngine.FindView(context, "baz");
|
||||
|
||||
// Assert
|
||||
Assert.True(result.Success);
|
||||
// Assert 1
|
||||
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();
|
||||
cacheMock.Verify();
|
||||
expander.Verify();
|
||||
}
|
||||
|
||||
// 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.
|
||||
[Fact]
|
||||
public void FindView_DoesNotInvokeExpandViewLocations_IfCacheEntryMatchesButViewIsNotFound()
|
||||
public void FindView_DoesNotInvokeViewLocationExpanders_IfChangeTokenHasNotExpired()
|
||||
{
|
||||
// Arrange
|
||||
var pageFactory = Mock.Of<IRazorPageFactory>();
|
||||
var viewFactory = Mock.Of<IRazorViewFactory>();
|
||||
var cache = new DefaultViewLocationCache();
|
||||
var pageFactory = Mock.Of<IRazorPageFactoryProvider>();
|
||||
var expander = new Mock<IViewLocationExpander>();
|
||||
var expandedLocations = new[]
|
||||
{
|
||||
|
|
@ -548,16 +635,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
.Verifiable();
|
||||
expander
|
||||
.Setup(v => v.ExpandViewLocations(
|
||||
It.IsAny<ViewLocationExpanderContext>(), It.IsAny<IEnumerable<string>>()))
|
||||
.Returns((ViewLocationExpanderContext c, IEnumerable<string> viewLocations) => expandedLocations)
|
||||
It.IsAny<ViewLocationExpanderContext>(),
|
||||
It.IsAny<IEnumerable<string>>()))
|
||||
.Returns(expandedLocations)
|
||||
.Verifiable();
|
||||
|
||||
|
||||
var viewEngine = CreateViewEngine(
|
||||
pageFactory,
|
||||
viewFactory,
|
||||
expanders: new[] { expander.Object },
|
||||
cache: cache);
|
||||
expanders: new[] { expander.Object });
|
||||
var context = GetActionContext(_controllerTestContext);
|
||||
|
||||
// Act - 1
|
||||
|
|
@ -582,6 +667,70 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
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]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
|
|
@ -604,36 +753,39 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
{
|
||||
// Arrange
|
||||
var page = Mock.Of<IRazorPage>();
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("expanded-path/bar-layout"))
|
||||
.Returns(page)
|
||||
.Verifiable();
|
||||
|
||||
var viewFactory = new Mock<IRazorViewFactory>(MockBehavior.Strict);
|
||||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("expanded-path/bar-layout"))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Verifiable();
|
||||
|
||||
var expander = new Mock<IViewLocationExpander>();
|
||||
expander.Setup(e => e.PopulateValues(It.IsAny<ViewLocationExpanderContext>()))
|
||||
.Callback((ViewLocationExpanderContext c) =>
|
||||
{
|
||||
Assert.NotNull(c.ActionContext);
|
||||
c.Values["expander-key"] = expander.ToString();
|
||||
})
|
||||
.Verifiable();
|
||||
expander.Setup(e => e.ExpandViewLocations(It.IsAny<ViewLocationExpanderContext>(),
|
||||
It.IsAny<IEnumerable<string>>()))
|
||||
.Returns((ViewLocationExpanderContext c, IEnumerable<string> seeds) =>
|
||||
{
|
||||
Assert.NotNull(c.ActionContext);
|
||||
Assert.Equal(expectedSeeds, seeds);
|
||||
expander
|
||||
.Setup(e => e.PopulateValues(It.IsAny<ViewLocationExpanderContext>()))
|
||||
.Callback((ViewLocationExpanderContext c) =>
|
||||
{
|
||||
Assert.NotNull(c.ActionContext);
|
||||
c.Values["expander-key"] = expander.ToString();
|
||||
})
|
||||
.Verifiable();
|
||||
expander
|
||||
.Setup(e => e.ExpandViewLocations(
|
||||
It.IsAny<ViewLocationExpanderContext>(),
|
||||
It.IsAny<IEnumerable<string>>()))
|
||||
.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}" };
|
||||
})
|
||||
.Verifiable();
|
||||
return new[] { "expanded-path/bar-{0}" };
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var viewEngine = CreateViewEngine(pageFactory.Object, viewFactory.Object,
|
||||
new[] { expander.Object });
|
||||
var viewEngine = CreateViewEngine(
|
||||
pageFactory.Object,
|
||||
new[] { expander.Object });
|
||||
var context = GetActionContext(routeValues);
|
||||
|
||||
// Act
|
||||
|
|
@ -686,10 +838,11 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
};
|
||||
|
||||
var page = new Mock<IRazorPage>(MockBehavior.Strict).Object;
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("/Views/Foo/details.cshtml"))
|
||||
.Returns(page)
|
||||
.Verifiable();
|
||||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory("/Views/Foo/details.cshtml"))
|
||||
.Returns(new RazorPageFactoryResult(() => page, new IChangeToken[0]))
|
||||
.Verifiable();
|
||||
|
||||
var viewEngine = CreateViewEngine(pageFactory.Object);
|
||||
var routesInActionDescriptor = new Dictionary<string, string>()
|
||||
|
|
@ -1092,23 +1245,14 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
}
|
||||
}
|
||||
|
||||
private RazorViewEngine CreateViewEngine(
|
||||
IRazorPageFactory pageFactory = null,
|
||||
IRazorViewFactory viewFactory = null,
|
||||
IEnumerable<IViewLocationExpander> expanders = null,
|
||||
IViewLocationCache cache = null)
|
||||
private TestableRazorViewEngine CreateViewEngine(
|
||||
IRazorPageFactoryProvider pageFactory = null,
|
||||
IEnumerable<IViewLocationExpander> expanders = null)
|
||||
{
|
||||
pageFactory = pageFactory ?? Mock.Of<IRazorPageFactory>();
|
||||
viewFactory = viewFactory ?? Mock.Of<IRazorViewFactory>();
|
||||
|
||||
cache = cache ?? GetViewLocationCache();
|
||||
|
||||
var viewEngine = new RazorViewEngine(pageFactory,
|
||||
viewFactory,
|
||||
GetOptionsAccessor(expanders),
|
||||
cache);
|
||||
|
||||
return viewEngine;
|
||||
pageFactory = pageFactory ?? Mock.Of<IRazorPageFactoryProvider>();
|
||||
return new TestableRazorViewEngine(
|
||||
pageFactory,
|
||||
GetOptionsAccessor(expanders));
|
||||
}
|
||||
|
||||
private static IOptions<RazorViewEngineOptions> GetOptionsAccessor(
|
||||
|
|
@ -1129,15 +1273,6 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
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)
|
||||
{
|
||||
var httpContext = new DefaultHttpContext();
|
||||
|
|
@ -1185,32 +1320,33 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
return new ActionContext(httpContext, routeData, actionDescriptor);
|
||||
}
|
||||
|
||||
private class OverloadedLocationViewEngine : RazorViewEngine
|
||||
private class TestableRazorViewEngine : RazorViewEngine
|
||||
{
|
||||
public OverloadedLocationViewEngine(
|
||||
IRazorPageFactory pageFactory,
|
||||
IRazorViewFactory viewFactory,
|
||||
IOptions<RazorViewEngineOptions> optionsAccessor,
|
||||
IViewLocationCache cache)
|
||||
: base(pageFactory, viewFactory, optionsAccessor, cache)
|
||||
private IEnumerable<string> _viewLocationFormats;
|
||||
private IEnumerable<string> _areaViewLocationFormats;
|
||||
|
||||
public TestableRazorViewEngine(
|
||||
IRazorPageFactoryProvider pageFactory,
|
||||
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
|
||||
{
|
||||
return new[] { "fake-path1/{1}/{0}.rzr" };
|
||||
}
|
||||
_viewLocationFormats = viewLocationFormats;
|
||||
_areaViewLocationFormats = areaViewLocationFormats;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> AreaViewLocationFormats
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[] { "fake-area-path/{2}/{1}/{0}.rzr" };
|
||||
}
|
||||
}
|
||||
public override IEnumerable<string> ViewLocationFormats =>
|
||||
_viewLocationFormats != null ? _viewLocationFormats : base.ViewLocationFormats;
|
||||
|
||||
public override IEnumerable<string> AreaViewLocationFormats =>
|
||||
_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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http.Features;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
|
|
@ -14,6 +13,7 @@ using Microsoft.AspNet.Mvc.Rendering;
|
|||
using Microsoft.AspNet.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNet.PageExecutionInstrumentation;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Extensions.WebEncoders.Testing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
|
@ -39,12 +39,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
v.HtmlEncoder = new HtmlTestEncoder();
|
||||
v.Write("Hello world");
|
||||
});
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: true);
|
||||
var view = new RazorView(
|
||||
Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: true);
|
||||
var viewContext = CreateViewContext(view);
|
||||
var expected = viewContext.Writer;
|
||||
|
||||
|
|
@ -68,12 +69,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
Assert.Same(viewData, v.ViewContext.ViewData);
|
||||
});
|
||||
var activator = new Mock<IRazorPageActivator>();
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
activator.Object,
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: true);
|
||||
var view = new RazorView(
|
||||
Mock.Of<IRazorViewEngine>(),
|
||||
activator.Object,
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: true);
|
||||
|
||||
var viewContext = CreateViewContext(view);
|
||||
var expectedWriter = viewContext.Writer;
|
||||
|
|
@ -132,12 +134,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
viewEngine.Setup(v => v.FindPage(It.IsAny<ActionContext>(), LayoutPath))
|
||||
.Returns(new RazorPageResult(LayoutPath, layout));
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
activator,
|
||||
CreateViewStartProvider(viewStart),
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
var view = new RazorView(
|
||||
viewEngine.Object,
|
||||
activator,
|
||||
new[] { viewStart },
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
||||
var viewContext = CreateViewContext(view);
|
||||
var expectedWriter = viewContext.Writer;
|
||||
|
|
@ -157,12 +160,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var activator = new Mock<IRazorPageActivator>();
|
||||
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
|
||||
.Verifiable();
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
activator.Object,
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: true);
|
||||
var view = new RazorView(
|
||||
Mock.Of<IRazorViewEngine>(),
|
||||
activator.Object,
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: true);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act
|
||||
|
|
@ -193,29 +197,28 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
v.Write("layout-content" + Environment.NewLine);
|
||||
v.RenderBodyPublic();
|
||||
});
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance(LayoutPath))
|
||||
.Returns(layout);
|
||||
var pageFactory = new Mock<IRazorPageFactoryProvider>();
|
||||
pageFactory
|
||||
.Setup(p => p.CreateFactory(LayoutPath))
|
||||
.Returns(new RazorPageFactoryResult(() => layout, new IChangeToken[0]));
|
||||
|
||||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
viewEngine.Setup(v => v.FindPage(It.IsAny<ActionContext>(), LayoutPath))
|
||||
.Returns(new RazorPageResult(LayoutPath, layout));
|
||||
|
||||
var viewStartProvider = CreateViewStartProvider();
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
viewStartProvider,
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: true);
|
||||
var view = new RazorView(
|
||||
viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: true);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act
|
||||
await view.RenderAsync(viewContext);
|
||||
|
||||
// Assert
|
||||
Mock.Get(viewStartProvider)
|
||||
.Verify(v => v.GetViewStartPages(It.IsAny<string>()), Times.Never());
|
||||
Assert.Equal(expected, viewContext.Writer.ToString());
|
||||
}
|
||||
|
||||
|
|
@ -228,12 +231,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
actual = v.Output;
|
||||
});
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
var view = new RazorView(
|
||||
Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
var viewContext = CreateViewContext(view);
|
||||
var original = viewContext.Writer;
|
||||
|
||||
|
|
@ -253,12 +257,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
v.WriteLiteral("Hello world");
|
||||
});
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
var view = new RazorView(
|
||||
Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
var viewContext = CreateViewContext(view);
|
||||
var original = viewContext.Writer;
|
||||
|
||||
|
|
@ -280,12 +285,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var activator = new Mock<IRazorPageActivator>();
|
||||
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
|
||||
.Verifiable();
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
activator.Object,
|
||||
CreateViewStartProvider(),
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
var view = new RazorView(
|
||||
Mock.Of<IRazorViewEngine>(),
|
||||
activator.Object,
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act
|
||||
|
|
@ -323,12 +329,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
.Verifiable();
|
||||
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
|
||||
.Verifiable();
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
activator.Object,
|
||||
CreateViewStartProvider(viewStart1, viewStart2),
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
var view = new RazorView(
|
||||
Mock.Of<IRazorViewEngine>(),
|
||||
activator.Object,
|
||||
new[] { viewStart1, viewStart2 },
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act
|
||||
|
|
@ -358,7 +365,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var activator = new Mock<IRazorPageActivator>();
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
Mock.Of<IViewStartProvider>(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -422,7 +429,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
activator.Object,
|
||||
CreateViewStartProvider(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -464,7 +471,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -527,7 +534,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -587,7 +594,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -647,7 +654,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -712,7 +719,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -744,7 +751,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -808,7 +815,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -844,7 +851,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -889,7 +896,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -956,7 +963,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -1009,7 +1016,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -1059,7 +1066,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -1089,7 +1096,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -1134,7 +1141,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -1204,13 +1211,9 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "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,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
viewStartProvider.Object,
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -1222,7 +1225,6 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
// Assert
|
||||
feature.Verify();
|
||||
viewStartProvider.Verify();
|
||||
Assert.True(layoutExecuted);
|
||||
}
|
||||
|
||||
|
|
@ -1256,7 +1258,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
Mock.Of<IViewStartProvider>(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: true);
|
||||
|
|
@ -1288,7 +1290,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
Mock.Of<IViewStartProvider>(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial);
|
||||
|
|
@ -1328,7 +1330,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var view = new RazorView(viewEngine,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(viewStart1, viewStart2),
|
||||
new[] { viewStart1, viewStart2 },
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -1364,12 +1366,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
});
|
||||
var viewEngine = Mock.Of<IRazorViewEngine>();
|
||||
|
||||
var view = new RazorView(viewEngine,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(viewStart1, viewStart2),
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
var view = new RazorView(
|
||||
viewEngine,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
new[] { viewStart1, viewStart2 },
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act
|
||||
|
|
@ -1402,13 +1405,14 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
v.RenderBodyPublic();
|
||||
});
|
||||
var viewEngine = new Mock<IRazorViewEngine>();
|
||||
viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "/Layout.cshtml"))
|
||||
.Returns(new RazorPageResult("Layout", layout));
|
||||
viewEngine
|
||||
.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "/Layout.cshtml"))
|
||||
.Returns(new RazorPageResult("Layout", layout));
|
||||
|
||||
var view = new RazorView(
|
||||
viewEngine.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(viewStart),
|
||||
new[] { viewStart },
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: false);
|
||||
|
|
@ -1434,7 +1438,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
});
|
||||
var view = new RazorView(Mock.Of<IRazorViewEngine>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider(),
|
||||
new IRazorPage[0],
|
||||
page,
|
||||
new HtmlTestEncoder(),
|
||||
isPartial: true);
|
||||
|
|
@ -1447,6 +1451,48 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
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()
|
||||
{
|
||||
var mockWriter = new Mock<TextWriter>();
|
||||
|
|
@ -1469,17 +1515,6 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
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 readonly Action<TestableRazorPage> _executeAction;
|
||||
|
|
|
|||
Loading…
Reference in New Issue