зеркало из https://github.com/aspnet/Mvc.git
Changes to make EnableInstrumentation conditionally enabled
This commit is contained in:
Родитель
5d32d224f4
Коммит
12477c9f52
15
Mvc.sln
15
Mvc.sln
|
@ -1,7 +1,7 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.22013.1
|
||||
VisualStudioVersion = 14.0.22130.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{DAAE4C74-D06F-4874-A166-33305D2643CE}"
|
||||
EndProject
|
||||
|
@ -86,6 +86,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ReflectedModelWebSite", "te
|
|||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "FilesWebSite", "test\WebSites\FilesWebSite\FilesWebSite.kproj", "{0EF9860B-10D7-452F-B0F4-A405B88BEBB3}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "RazorInstrumentationWebSite", "test\WebSites\RazorInstrumentationWebSite\RazorInstrumentationWebSite.kproj", "{2B2B9876-903C-4065-8D62-2EE832BBA106}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -446,6 +448,16 @@ Global
|
|||
{C2EF54F8-8886-4260-A322-44F76245F95D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{C2EF54F8-8886-4260-A322-44F76245F95D}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{C2EF54F8-8886-4260-A322-44F76245F95D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2B2B9876-903C-4065-8D62-2EE832BBA106}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2B2B9876-903C-4065-8D62-2EE832BBA106}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2B2B9876-903C-4065-8D62-2EE832BBA106}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{2B2B9876-903C-4065-8D62-2EE832BBA106}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{2B2B9876-903C-4065-8D62-2EE832BBA106}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{2B2B9876-903C-4065-8D62-2EE832BBA106}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2B2B9876-903C-4065-8D62-2EE832BBA106}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2B2B9876-903C-4065-8D62-2EE832BBA106}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{2B2B9876-903C-4065-8D62-2EE832BBA106}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{2B2B9876-903C-4065-8D62-2EE832BBA106}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -487,5 +499,6 @@ Global
|
|||
{0EF9860B-10D7-452F-B0F4-A405B88BEBB3} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||
{61061528-071E-424E-965A-07BCC2F02672} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||
{C2EF54F8-8886-4260-A322-44F76245F95D} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||
{2B2B9876-903C-4065-8D62-2EE832BBA106} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -64,25 +64,30 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
return false;
|
||||
}
|
||||
|
||||
public CompilationResult GetOrAdd(RelativeFileInfo fileInfo, Func<CompilationResult> compile)
|
||||
public CompilationResult GetOrAdd([NotNull] RelativeFileInfo fileInfo,
|
||||
bool enableInstrumentation,
|
||||
[NotNull] Func<CompilationResult> compile)
|
||||
{
|
||||
CompilerCacheEntry cacheEntry;
|
||||
if (!_cache.TryGetValue(fileInfo.RelativePath, out cacheEntry))
|
||||
{
|
||||
return OnCacheMiss(fileInfo, compile);
|
||||
return OnCacheMiss(fileInfo, enableInstrumentation, compile);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cacheEntry.Length != fileInfo.FileInfo.Length)
|
||||
if ((cacheEntry.Length != fileInfo.FileInfo.Length) ||
|
||||
(enableInstrumentation && !cacheEntry.IsInstrumented))
|
||||
{
|
||||
// it's not a match, recompile
|
||||
return OnCacheMiss(fileInfo, compile);
|
||||
// Recompile if
|
||||
// (a) If the file lengths differ
|
||||
// (b) If the compiled type is not instrumented but we require it to be instrumented.
|
||||
return OnCacheMiss(fileInfo, enableInstrumentation, compile);
|
||||
}
|
||||
|
||||
if (cacheEntry.LastModified == fileInfo.FileInfo.LastModified)
|
||||
{
|
||||
// Match, not update needed
|
||||
return CompilationResult.Successful(cacheEntry.ViewType);
|
||||
return CompilationResult.Successful(cacheEntry.CompiledType);
|
||||
}
|
||||
|
||||
var hash = RazorFileHash.GetHash(fileInfo.FileInfo);
|
||||
|
@ -92,20 +97,24 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
string.Equals(cacheEntry.Hash, hash, StringComparison.Ordinal))
|
||||
{
|
||||
// Cache hit, but we need to update the entry
|
||||
return OnCacheMiss(fileInfo, () => CompilationResult.Successful(cacheEntry.ViewType));
|
||||
return OnCacheMiss(fileInfo,
|
||||
enableInstrumentation,
|
||||
() => CompilationResult.Successful(cacheEntry.CompiledType));
|
||||
}
|
||||
|
||||
// it's not a match, recompile
|
||||
return OnCacheMiss(fileInfo, compile);
|
||||
return OnCacheMiss(fileInfo, enableInstrumentation, compile);
|
||||
}
|
||||
}
|
||||
|
||||
private CompilationResult OnCacheMiss(RelativeFileInfo file, Func<CompilationResult> compile)
|
||||
private CompilationResult OnCacheMiss(RelativeFileInfo file,
|
||||
bool isInstrumented,
|
||||
Func<CompilationResult> compile)
|
||||
{
|
||||
var result = compile();
|
||||
|
||||
var cacheEntry = new CompilerCacheEntry(file, result.CompiledType);
|
||||
_cache.AddOrUpdate(file.RelativePath, cacheEntry, (a, b) => cacheEntry);
|
||||
var cacheEntry = new CompilerCacheEntry(file, result.CompiledType, isInstrumented);
|
||||
_cache[file.RelativePath] = cacheEntry;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -5,35 +5,74 @@ using System;
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
/// <summary>
|
||||
/// An entry in <see cref="CompilerCache"/> that contain metadata about precompiled and dynamically compiled file.
|
||||
/// </summary>
|
||||
public class CompilerCacheEntry
|
||||
{
|
||||
public CompilerCacheEntry([NotNull] RazorFileInfo info, [NotNull] Type viewType)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="CompilerCacheEntry"/> for a file that was precompiled.
|
||||
/// </summary>
|
||||
/// <param name="info">Metadata about the precompiled file.</param>
|
||||
/// <param name="compiledType">The compiled <see cref="Type"/>.</param>
|
||||
public CompilerCacheEntry([NotNull] RazorFileInfo info, [NotNull] Type compiledType)
|
||||
{
|
||||
ViewType = viewType;
|
||||
CompiledType = compiledType;
|
||||
RelativePath = info.RelativePath;
|
||||
Length = info.Length;
|
||||
LastModified = info.LastModified;
|
||||
Hash = info.Hash;
|
||||
}
|
||||
|
||||
public CompilerCacheEntry([NotNull] RelativeFileInfo info, [NotNull] Type viewType)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="CompilerCacheEntry"/> for a file that was dynamically compiled.
|
||||
/// </summary>
|
||||
/// <param name="info">Metadata about the file that was compiled.</param>
|
||||
/// <param name="compiledType">The compiled <see cref="Type"/>.</param>
|
||||
/// <param name="isInstrumented">Flag that indicates that the file was generated with instrumentation
|
||||
/// enabled.</param>
|
||||
public CompilerCacheEntry([NotNull] RelativeFileInfo info, [NotNull] Type compiledType, bool isInstrumented)
|
||||
{
|
||||
ViewType = viewType;
|
||||
CompiledType = compiledType;
|
||||
RelativePath = info.RelativePath;
|
||||
Length = info.FileInfo.Length;
|
||||
LastModified = info.FileInfo.LastModified;
|
||||
IsInstrumented = isInstrumented;
|
||||
}
|
||||
|
||||
public Type ViewType { get; set; }
|
||||
public string RelativePath { get; set; }
|
||||
public long Length { get; set; }
|
||||
public DateTime LastModified { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Type"/> produced as a result of compilation.
|
||||
/// </summary>
|
||||
public Type CompiledType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The file hash, should only be available for pre compiled files.
|
||||
/// Gets the path of the compiled file relative to the root of the application.
|
||||
/// </summary>
|
||||
public string Hash { get; set; }
|
||||
public string RelativePath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of file (in bytes) on disk.
|
||||
/// </summary>
|
||||
public long Length { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last modified <see cref="DateTime"/> for the file that was compiled at the time of compilation.
|
||||
/// </summary>
|
||||
public DateTime LastModified { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file hash, should only be available for pre compiled files.
|
||||
/// </summary>
|
||||
public string Hash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a flag that indicates if the file is precompiled.
|
||||
/// </summary>
|
||||
public bool IsPreCompiled { get { return Hash != null; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a flag that indiciates if the page execution in <see cref="CompiledType"/> is instrumeted.
|
||||
/// </summary>
|
||||
public bool IsInstrumented { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,8 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// Creates a <see cref="IRazorPage"/> for the specified path.
|
||||
/// </summary>
|
||||
/// <param name="relativePath">The path to locate the page.</param>
|
||||
/// <param name="enableInstrumentation">Indicates that execution of the page should be instrumented.</param>
|
||||
/// <returns>The IRazorPage instance if it exists, null otherwise.</returns>
|
||||
IRazorPage CreateInstance(string relativePath);
|
||||
IRazorPage CreateInstance(string relativePath, bool enableInstrumentation);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.AspNet.PageExecutionInstrumentation;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
|
@ -17,6 +18,8 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// </summary>
|
||||
/// <param name="razorPage">The <see cref="IRazorPage"/> instance to execute.</param>
|
||||
/// <param name="isPartial">Determines if the view is to be executed as a partial.</param>
|
||||
void Contextualize(IRazorPage razorPage, bool isPartial);
|
||||
void Contextualize(IRazorPage razorPage,
|
||||
bool isPartial,
|
||||
IPageExecutionListenerFeature pageExecutionListenerFeature);
|
||||
}
|
||||
}
|
|
@ -15,7 +15,8 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
/// that are applicable to the specified view.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the page to locate ViewStart files for.</param>
|
||||
/// <param name="enableInstrumentation">Indicates that execution of the page should be instrumented.</param>
|
||||
/// <returns>A sequence of <see cref="IRazorPage"/> that represent ViewStart.</returns>
|
||||
IEnumerable<IRazorPage> GetViewStartPages(string path);
|
||||
IEnumerable<IRazorPage> GetViewStartPages(string path, bool enableInstrumentation);
|
||||
}
|
||||
}
|
|
@ -3,8 +3,19 @@
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the contracts for a service that compiles Razor files.
|
||||
/// </summary>
|
||||
public interface IRazorCompilationService
|
||||
{
|
||||
CompilationResult Compile(RelativeFileInfo fileInfo);
|
||||
/// <summary>
|
||||
/// Compiles the razor file located at <paramref name="fileInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo">A <see cref="RelativeFileInfo"/> instance that represents the file to compile.
|
||||
/// </param>
|
||||
/// <param name="isInstrumented">Indicates that the page should be instrumented.</param>
|
||||
/// <returns>A <see cref="CompilationResult"/> that represents the results of parsing and compiling the file.
|
||||
/// </returns>
|
||||
CompilationResult Compile(RelativeFileInfo fileInfo, bool isInstrumented);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,14 @@ using Microsoft.AspNet.Razor;
|
|||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation of <see cref="IRazorCompilationService"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class must be registered as a singleton service for the caching to work.
|
||||
/// </remarks>
|
||||
public class RazorCompilationService : IRazorCompilationService
|
||||
{
|
||||
// This class must be registered as a singleton service for the caching to work.
|
||||
private readonly CompilerCache _cache;
|
||||
private readonly ICompilationService _baseCompilationService;
|
||||
private readonly IMvcRazorHost _razorHost;
|
||||
|
@ -22,13 +27,16 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
_cache = new CompilerCache(_controllerAssemblyProvider.CandidateAssemblies);
|
||||
}
|
||||
|
||||
public CompilationResult Compile([NotNull] RelativeFileInfo file)
|
||||
/// <inheritdoc />
|
||||
public CompilationResult Compile([NotNull] RelativeFileInfo file, bool isInstrumented)
|
||||
{
|
||||
return _cache.GetOrAdd(file, () => CompileCore(file));
|
||||
return _cache.GetOrAdd(file, isInstrumented, () => CompileCore(file, isInstrumented));
|
||||
}
|
||||
|
||||
internal CompilationResult CompileCore(RelativeFileInfo file)
|
||||
internal CompilationResult CompileCore(RelativeFileInfo file, bool isInstrumented)
|
||||
{
|
||||
_razorHost.EnableInstrumentation = isInstrumented;
|
||||
|
||||
GeneratorResults results;
|
||||
using (var inputStream = file.FileInfo.CreateReadStream())
|
||||
{
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.PageExecutionInstrumentation;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
{
|
||||
|
@ -15,6 +17,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
private readonly IRazorPageFactory _pageFactory;
|
||||
private readonly IRazorPageActivator _pageActivator;
|
||||
private readonly IViewStartProvider _viewStartProvider;
|
||||
private IPageExecutionListenerFeature _pageExecutionFeature;
|
||||
private IRazorPage _razorPage;
|
||||
private bool _isPartial;
|
||||
|
||||
|
@ -34,11 +37,19 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
_viewStartProvider = viewStartProvider;
|
||||
}
|
||||
|
||||
private bool EnableInstrumentation
|
||||
{
|
||||
get { return _pageExecutionFeature != null; }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Contextualize(IRazorPage razorPage, bool isPartial)
|
||||
public virtual void Contextualize([NotNull] IRazorPage razorPage,
|
||||
bool isPartial,
|
||||
IPageExecutionListenerFeature pageExecutionListener)
|
||||
{
|
||||
_razorPage = razorPage;
|
||||
_isPartial = isPartial;
|
||||
_pageExecutionFeature = pageExecutionListener;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -61,45 +72,66 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
}
|
||||
|
||||
private async Task<RazorTextWriter> RenderPageAsync(IRazorPage page,
|
||||
ViewContext context,
|
||||
bool executeViewStart)
|
||||
private async Task<IBufferedTextWriter> RenderPageAsync(IRazorPage page,
|
||||
ViewContext context,
|
||||
bool executeViewStart)
|
||||
{
|
||||
using (var bufferedWriter = new RazorTextWriter(context.Writer, context.Writer.Encoding))
|
||||
var razorTextWriter = new RazorTextWriter(context.Writer, context.Writer.Encoding);
|
||||
TextWriter writer = razorTextWriter;
|
||||
IBufferedTextWriter bufferedWriter = razorTextWriter;
|
||||
|
||||
if (EnableInstrumentation)
|
||||
{
|
||||
// The writer for the body is passed through the ViewContext, allowing things like HtmlHelpers
|
||||
// and ViewComponents to reference it.
|
||||
var oldWriter = context.Writer;
|
||||
context.Writer = bufferedWriter;
|
||||
|
||||
try
|
||||
writer = _pageExecutionFeature.DecorateWriter(razorTextWriter);
|
||||
bufferedWriter = writer as IBufferedTextWriter;
|
||||
if (bufferedWriter == null)
|
||||
{
|
||||
if (executeViewStart)
|
||||
{
|
||||
// Execute view starts using the same context + writer as the page to render.
|
||||
await RenderViewStartAsync(context);
|
||||
}
|
||||
var message = Resources.FormatInstrumentation_WriterMustBeBufferedTextWriter(
|
||||
nameof(TextWriter),
|
||||
_pageExecutionFeature.GetType().FullName,
|
||||
typeof(IBufferedTextWriter).FullName);
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
}
|
||||
|
||||
await RenderPageCoreAsync(page, context);
|
||||
return bufferedWriter;
|
||||
}
|
||||
finally
|
||||
// The writer for the body is passed through the ViewContext, allowing things like HtmlHelpers
|
||||
// and ViewComponents to reference it.
|
||||
var oldWriter = context.Writer;
|
||||
context.Writer = writer;
|
||||
|
||||
try
|
||||
{
|
||||
if (executeViewStart)
|
||||
{
|
||||
context.Writer = oldWriter;
|
||||
// Execute view starts using the same context + writer as the page to render.
|
||||
await RenderViewStartAsync(context);
|
||||
}
|
||||
|
||||
await RenderPageCoreAsync(page, context);
|
||||
return bufferedWriter;
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.Writer = oldWriter;
|
||||
writer.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RenderPageCoreAsync(IRazorPage page, ViewContext context)
|
||||
{
|
||||
page.ViewContext = context;
|
||||
if (EnableInstrumentation)
|
||||
{
|
||||
page.PageExecutionContext = _pageExecutionFeature.GetContext(page.Path, context.Writer);
|
||||
}
|
||||
|
||||
_pageActivator.Activate(page, context);
|
||||
await page.ExecuteAsync();
|
||||
}
|
||||
|
||||
private async Task RenderViewStartAsync(ViewContext context)
|
||||
{
|
||||
var viewStarts = _viewStartProvider.GetViewStartPages(_razorPage.Path);
|
||||
var viewStarts = _viewStartProvider.GetViewStartPages(_razorPage.Path, EnableInstrumentation);
|
||||
|
||||
foreach (var viewStart in viewStarts)
|
||||
{
|
||||
|
@ -111,7 +143,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
|
||||
private async Task RenderLayoutAsync(ViewContext context,
|
||||
RazorTextWriter bodyWriter)
|
||||
IBufferedTextWriter bodyWriter)
|
||||
{
|
||||
// A layout page can specify another layout page. We'll need to continue
|
||||
// looking for layout pages until they're no longer specified.
|
||||
|
@ -129,7 +161,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
var layoutPage = _pageFactory.CreateInstance(previousPage.Layout);
|
||||
var layoutPage = _pageFactory.CreateInstance(previousPage.Layout, EnableInstrumentation);
|
||||
if (layoutPage == null)
|
||||
{
|
||||
var message = Resources.FormatLayoutCannotBeLocated(previousPage.Layout);
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNet.Mvc.Razor.OptionDescriptors;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.AspNet.PageExecutionInstrumentation;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Razor
|
||||
|
@ -35,6 +37,9 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
private readonly IRazorPageFactory _pageFactory;
|
||||
private readonly IReadOnlyList<IViewLocationExpander> _viewLocationExpanders;
|
||||
private readonly IViewLocationCache _viewLocationCache;
|
||||
// The RazorViewEngine is Request scoped which allows us to cache these value for the lifetime of a Request.
|
||||
private bool _isPageExecutionFeatureInitialized;
|
||||
private IPageExecutionListenerFeature _pageExecutionListenerFeature;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RazorViewEngine" /> class.
|
||||
|
@ -90,7 +95,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
if (viewName.EndsWith(ViewExtension, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var page = _pageFactory.CreateInstance(viewName);
|
||||
var page = _pageFactory.CreateInstance(viewName, IsInstrumentationEnabled(context));
|
||||
if (page != null)
|
||||
{
|
||||
return CreateFoundResult(context, page, viewName, partial);
|
||||
|
@ -132,7 +137,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var viewLocation = _viewLocationCache.Get(expanderContext);
|
||||
if (!string.IsNullOrEmpty(viewLocation))
|
||||
{
|
||||
var page = _pageFactory.CreateInstance(viewLocation);
|
||||
var page = _pageFactory.CreateInstance(viewLocation, IsInstrumentationEnabled(context));
|
||||
|
||||
if (page != null)
|
||||
{
|
||||
|
@ -158,7 +163,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
viewName,
|
||||
controllerName,
|
||||
areaName);
|
||||
var page = _pageFactory.CreateInstance(transformedPath);
|
||||
var page = _pageFactory.CreateInstance(transformedPath, IsInstrumentationEnabled(context));
|
||||
if (page != null)
|
||||
{
|
||||
// 3a. We found a page. Cache the set of values that produced it and return a found result.
|
||||
|
@ -183,7 +188,9 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
var services = actionContext.HttpContext.RequestServices;
|
||||
var view = services.GetService<IRazorView>();
|
||||
view.Contextualize(page, partial);
|
||||
Debug.Assert(_isPageExecutionFeatureInitialized, "IsInstrumentationEnabled must be called prior to this.");
|
||||
|
||||
view.Contextualize(page, partial, _pageExecutionListenerFeature);
|
||||
return ViewEngineResult.Found(viewName, view);
|
||||
}
|
||||
|
||||
|
@ -191,5 +198,16 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
{
|
||||
return name[0] == '~' || name[0] == '/';
|
||||
}
|
||||
|
||||
private bool IsInstrumentationEnabled(ActionContext context)
|
||||
{
|
||||
if (!_isPageExecutionFeatureInitialized)
|
||||
{
|
||||
_isPageExecutionFeatureInitialized = true;
|
||||
_pageExecutionListenerFeature = context.HttpContext.GetFeature<IPageExecutionListenerFeature>();
|
||||
}
|
||||
|
||||
return _pageExecutionListenerFeature != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,14 +22,13 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
_pageFactory = pageFactory;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRazorPage> GetViewStartPages([NotNull] string path)
|
||||
public IEnumerable<IRazorPage> GetViewStartPages([NotNull] string path, bool enableInstrumentation)
|
||||
{
|
||||
var viewStartLocations = ViewStartUtility.GetViewStartLocations(_fileSystem, path);
|
||||
var viewStarts = viewStartLocations.Select(_pageFactory.CreateInstance)
|
||||
.Where(p => p != null)
|
||||
.ToArray();
|
||||
var viewStarts = viewStartLocations.Select(p => _pageFactory.CreateInstance(p, enableInstrumentation))
|
||||
.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 ]
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRazorPage CreateInstance([NotNull] string relativePath)
|
||||
public IRazorPage CreateInstance([NotNull] string relativePath, bool enableInstrumentation)
|
||||
{
|
||||
var fileInfo = _fileInfoCache.GetFileInfo(relativePath);
|
||||
|
||||
|
@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
RelativePath = relativePath,
|
||||
};
|
||||
|
||||
var result = _compilationService.Compile(relativeFileInfo);
|
||||
var result = _compilationService.Compile(relativeFileInfo, enableInstrumentation);
|
||||
var page = (IRazorPage)_activator.CreateInstance(_serviceProvider, result.CompiledType);
|
||||
page.Path = relativePath;
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Mvc.Core;
|
||||
using Microsoft.AspNet.Mvc.Description;
|
||||
using Microsoft.AspNet.Mvc.Filters;
|
||||
using Microsoft.AspNet.Mvc.Internal;
|
||||
|
@ -49,7 +48,10 @@ namespace Microsoft.AspNet.Mvc
|
|||
|
||||
yield return describe.Singleton<ICompilationService, RoslynCompilationService>();
|
||||
yield return describe.Singleton<IRazorCompilationService, RazorCompilationService>();
|
||||
yield return describe.Singleton<IViewEngineProvider, DefaultViewEngineProvider>();
|
||||
|
||||
// The provider is inexpensive to initialize and provides ViewEngines that may require request
|
||||
// specific services.
|
||||
yield return describe.Transient<IViewEngineProvider, DefaultViewEngineProvider>();
|
||||
yield return describe.Scoped<ICompositeViewEngine, CompositeViewEngine>();
|
||||
yield return describe.Singleton<IViewStartProvider, ViewStartProvider>();
|
||||
yield return describe.Transient<IRazorView, RazorView>();
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Threading.Tasks;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.TestHost;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||
using RazorInstrumentationWebSite;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||
{
|
||||
public class RazorInstrumentationTests
|
||||
{
|
||||
private readonly IServiceProvider _services = TestHelper.CreateServices("RazorInstrumentationWebsite");
|
||||
private readonly Action<IApplicationBuilder> _app = new Startup().Configure;
|
||||
private readonly string _expected = string.Join(Environment.NewLine,
|
||||
@"<div>",
|
||||
@"2147483647",
|
||||
"",
|
||||
@"viewstart-content",
|
||||
@"<p class=""Hello world"">",
|
||||
@"page-content",
|
||||
@"</p>",
|
||||
@"</div>");
|
||||
private readonly IEnumerable<Tuple<int, int, bool>> _expectedLineMappings = new[]
|
||||
{
|
||||
Tuple.Create(93, 2, true),
|
||||
Tuple.Create(96, 16, false),
|
||||
Tuple.Create(112, 2, true),
|
||||
Tuple.Create(0, 2, true),
|
||||
Tuple.Create(2, 8, true),
|
||||
Tuple.Create(10, 16, false),
|
||||
Tuple.Create(26, 1, true),
|
||||
Tuple.Create(27, 21, true),
|
||||
Tuple.Create(0, 7, true),
|
||||
Tuple.Create(8, 12, false),
|
||||
Tuple.Create(20, 2, true),
|
||||
Tuple.Create(23, 12, false),
|
||||
Tuple.Create(35, 8, true),
|
||||
};
|
||||
|
||||
public static IEnumerable<object[]> ActionNames
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return new[] { "FullPath" };
|
||||
yield return new[] { "ViewDiscoveryPath" };
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ActionNames))]
|
||||
public async Task ViewsAreServedWithoutInstrumentationByDefault(string actionName)
|
||||
{
|
||||
// Arrange
|
||||
var context = new TestPageExecutionContext();
|
||||
var services = GetServiceProvider(context);
|
||||
var server = TestServer.Create(services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act
|
||||
var body = await client.GetStringAsync("http://localhost/Home/" + actionName);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(_expected, body.Trim());
|
||||
Assert.Empty(context.Values);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ActionNames))]
|
||||
public async Task ViewsAreInstrumentedWhenPageExecutionListenerFeatureIsEnabled(string actionName)
|
||||
{
|
||||
// Arrange
|
||||
var context = new TestPageExecutionContext();
|
||||
var services = GetServiceProvider(context);
|
||||
var server = TestServer.Create(services, _app);
|
||||
var client = server.CreateClient();
|
||||
client.DefaultRequestHeaders.Add("ENABLE-RAZOR-INSTRUMENTATION", "true");
|
||||
|
||||
// Act
|
||||
var body = await client.GetStringAsync("http://localhost/Home/" + actionName);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(_expected, body.Trim());
|
||||
Assert.Equal(_expectedLineMappings, context.Values);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ActionNames))]
|
||||
public async Task ViewsCanSwitchFromRegularToInstrumented(string actionName)
|
||||
{
|
||||
// Arrange - 1
|
||||
var context = new TestPageExecutionContext();
|
||||
var services = GetServiceProvider(context);
|
||||
var server = TestServer.Create(services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act - 1
|
||||
var body = await client.GetStringAsync("http://localhost/Home/" + actionName);
|
||||
|
||||
// Assert - 1
|
||||
Assert.Equal(_expected, body.Trim());
|
||||
Assert.Empty(context.Values);
|
||||
|
||||
// Arrange - 2
|
||||
client.DefaultRequestHeaders.Add("ENABLE-RAZOR-INSTRUMENTATION", "true");
|
||||
|
||||
// Act - 2
|
||||
body = await client.GetStringAsync("http://localhost/Home/" + actionName);
|
||||
|
||||
// Assert - 2
|
||||
Assert.Equal(_expected, body.Trim());
|
||||
Assert.Equal(_expectedLineMappings, context.Values);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SwitchingFromNonInstrumentedToInstrumentedWorksForLayoutAndViewStarts()
|
||||
{
|
||||
// Arrange - 1
|
||||
var context = new TestPageExecutionContext();
|
||||
var services = GetServiceProvider(context);
|
||||
var server = TestServer.Create(services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// Act - 1
|
||||
var body = await client.GetStringAsync("http://localhost/Home/FullPath");
|
||||
|
||||
// Assert - 1
|
||||
Assert.Equal(_expected, body.Trim());
|
||||
Assert.Empty(context.Values);
|
||||
|
||||
// Arrange - 2
|
||||
client.DefaultRequestHeaders.Add("ENABLE-RAZOR-INSTRUMENTATION", "true");
|
||||
|
||||
// Act - 2
|
||||
body = await client.GetStringAsync("http://localhost/Home/ViewDiscoveryPath");
|
||||
|
||||
// Assert - 2
|
||||
Assert.Equal(_expected, body.Trim());
|
||||
Assert.Equal(_expectedLineMappings, context.Values);
|
||||
}
|
||||
|
||||
private IServiceProvider GetServiceProvider(TestPageExecutionContext pageExecutionContext)
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddInstance(pageExecutionContext);
|
||||
return services.BuildServiceProvider(_services);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@
|
|||
"ReflectedModelWebSite": "",
|
||||
"RoutingWebSite": "",
|
||||
"RazorWebSite": "",
|
||||
"RazorInstrumentationWebsite": "",
|
||||
"ValueProvidersSite": "",
|
||||
"XmlSerializerWebSite": "",
|
||||
"UrlHelperWebSite": "",
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
};
|
||||
|
||||
// Act
|
||||
var actual = cache.GetOrAdd(runtimeFileInfo, () => expected);
|
||||
var actual = cache.GetOrAdd(runtimeFileInfo, false, () => expected);
|
||||
|
||||
// Assert
|
||||
Assert.Same(expected, actual);
|
||||
|
@ -140,7 +140,8 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
|
||||
// Act
|
||||
var actual = cache.GetOrAdd(runtimeFileInfo,
|
||||
() => CompilationResult.Successful(resultViewType));
|
||||
enableInstrumentation: false,
|
||||
compile: () => CompilationResult.Successful(resultViewType));
|
||||
|
||||
// Assert
|
||||
if (swapsPreCompile)
|
||||
|
@ -174,9 +175,9 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
};
|
||||
|
||||
// Act
|
||||
cache.GetOrAdd(runtimeFileInfo, () => uncachedResult);
|
||||
var actual1 = cache.GetOrAdd(runtimeFileInfo, () => uncachedResult);
|
||||
var actual2 = cache.GetOrAdd(runtimeFileInfo, () => uncachedResult);
|
||||
cache.GetOrAdd(runtimeFileInfo, false, () => uncachedResult);
|
||||
var actual1 = cache.GetOrAdd(runtimeFileInfo, false, () => uncachedResult);
|
||||
var actual2 = cache.GetOrAdd(runtimeFileInfo, false, () => uncachedResult);
|
||||
|
||||
// Assert
|
||||
Assert.NotSame(uncachedResult, actual1);
|
||||
|
@ -189,5 +190,40 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
Assert.Null(actual2.CompiledContent);
|
||||
Assert.Same(type, actual2.CompiledType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetOrAdd_IgnoresCache_IfCachedItemIsNotInstrumentedAndEnableInstrumentationIsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var lastModified = DateTime.UtcNow;
|
||||
var cache = new CompilerCache();
|
||||
var fileInfo = new Mock<IFileInfo>();
|
||||
fileInfo.SetupGet(f => f.PhysicalPath)
|
||||
.Returns("test");
|
||||
fileInfo.SetupGet(f => f.LastModified)
|
||||
.Returns(lastModified);
|
||||
var type = GetType();
|
||||
var uncachedResult1 = UncachedCompilationResult.Successful(type, "hello world");
|
||||
var uncachedResult2 = UncachedCompilationResult.Successful(typeof(object), "hello world");
|
||||
var uncachedResult3 = UncachedCompilationResult.Successful(typeof(Guid), "hello world");
|
||||
|
||||
var runtimeFileInfo = new RelativeFileInfo()
|
||||
{
|
||||
FileInfo = fileInfo.Object,
|
||||
RelativePath = "test",
|
||||
};
|
||||
|
||||
// Act
|
||||
cache.GetOrAdd(runtimeFileInfo, false, () => uncachedResult1);
|
||||
var actual1 = cache.GetOrAdd(runtimeFileInfo, true, () => uncachedResult2);
|
||||
var actual2 = cache.GetOrAdd(runtimeFileInfo, false, () => uncachedResult3);
|
||||
|
||||
// Assert
|
||||
Assert.Same(uncachedResult2, actual1);
|
||||
Assert.Same(typeof(object), actual1.CompiledType);
|
||||
|
||||
Assert.NotSame(actual2, uncachedResult3);
|
||||
Assert.Same(typeof(object), actual2.CompiledType);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
// Arrange
|
||||
var host = new Mock<IMvcRazorHost>();
|
||||
host.Setup(h => h.GenerateCode(@"views\index\home.cshtml", It.IsAny<Stream>()))
|
||||
.Returns(new GeneratorResults(new Block(new BlockBuilder { Type = BlockType.Comment }), new RazorError[0], new CodeBuilderResult("", new LineMapping[0])))
|
||||
.Returns(GetGeneratorResult())
|
||||
.Verifiable();
|
||||
|
||||
var ap = new Mock<IControllerAssemblyProvider>();
|
||||
|
@ -46,10 +46,51 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
};
|
||||
|
||||
// Act
|
||||
razorService.CompileCore(relativeFileInfo);
|
||||
razorService.CompileCore(relativeFileInfo, isInstrumented: false);
|
||||
|
||||
// Assert
|
||||
host.Verify();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public void CompileCoreSetsEnableInstrumentationOnHost(bool enableInstrumentation)
|
||||
{
|
||||
// Arrange
|
||||
var host = new Mock<IMvcRazorHost>();
|
||||
host.SetupAllProperties();
|
||||
host.Setup(h => h.GenerateCode(It.IsAny<string>(), It.IsAny<Stream>()))
|
||||
.Returns(GetGeneratorResult());
|
||||
var assemblyProvider = new Mock<IControllerAssemblyProvider>();
|
||||
assemblyProvider.SetupGet(e => e.CandidateAssemblies)
|
||||
.Returns(Enumerable.Empty<Assembly>());
|
||||
|
||||
var compiler = new Mock<ICompilationService>();
|
||||
compiler.Setup(c => c.Compile(It.IsAny<IFileInfo>(), It.IsAny<string>()))
|
||||
.Returns(CompilationResult.Successful(GetType()));
|
||||
|
||||
var razorService = new RazorCompilationService(compiler.Object, assemblyProvider.Object, host.Object);
|
||||
var relativeFileInfo = new RelativeFileInfo()
|
||||
{
|
||||
FileInfo = Mock.Of<IFileInfo>(),
|
||||
RelativePath = @"views\index\home.cshtml",
|
||||
};
|
||||
|
||||
// Act
|
||||
razorService.CompileCore(relativeFileInfo, isInstrumented: enableInstrumentation);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(enableInstrumentation, host.Object.EnableInstrumentation);
|
||||
}
|
||||
|
||||
private static GeneratorResults GetGeneratorResult()
|
||||
{
|
||||
return new GeneratorResults(
|
||||
new Block(
|
||||
new BlockBuilder { Type = BlockType.Comment }),
|
||||
new RazorError[0],
|
||||
new CodeBuilderResult("", new LineMapping[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -183,7 +183,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
// Arrange
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
var page = Mock.Of<IRazorPage>();
|
||||
pageFactory.Setup(p => p.CreateInstance(It.IsAny<string>()))
|
||||
pageFactory.Setup(p => p.CreateInstance(It.IsAny<string>(), false))
|
||||
.Returns(Mock.Of<IRazorPage>());
|
||||
var viewEngine = CreateViewEngine(pageFactory.Object);
|
||||
var context = GetActionContext(_controllerTestContext);
|
||||
|
@ -203,7 +203,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
// Arrange
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
var page = Mock.Of<IRazorPage>();
|
||||
pageFactory.Setup(p => p.CreateInstance("fake-path1/bar/test-view.rzr"))
|
||||
pageFactory.Setup(p => p.CreateInstance("fake-path1/bar/test-view.rzr", false))
|
||||
.Returns(Mock.Of<IRazorPage>())
|
||||
.Verifiable();
|
||||
var viewEngine = new OverloadedLocationViewEngine(pageFactory.Object,
|
||||
|
@ -224,7 +224,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
// Arrange
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
var page = Mock.Of<IRazorPage>();
|
||||
pageFactory.Setup(p => p.CreateInstance("fake-area-path/foo/bar/test-view2.rzr"))
|
||||
pageFactory.Setup(p => p.CreateInstance("fake-area-path/foo/bar/test-view2.rzr", false))
|
||||
.Returns(Mock.Of<IRazorPage>())
|
||||
.Verifiable();
|
||||
var viewEngine = new OverloadedLocationViewEngine(pageFactory.Object,
|
||||
|
@ -273,7 +273,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
{
|
||||
// Arrange
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("test-string/bar.cshtml"))
|
||||
pageFactory.Setup(p => p.CreateInstance("test-string/bar.cshtml", false))
|
||||
.Returns(Mock.Of<IRazorPage>())
|
||||
.Verifiable();
|
||||
var expander1Result = new[] { "some-seed" };
|
||||
|
@ -325,9 +325,9 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
{
|
||||
// Arrange
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("/Views/bar/baz.cshtml"))
|
||||
pageFactory.Setup(p => p.CreateInstance("/Views/bar/baz.cshtml", false))
|
||||
.Verifiable();
|
||||
pageFactory.Setup(p => p.CreateInstance("/Views/Shared/baz.cshtml"))
|
||||
pageFactory.Setup(p => p.CreateInstance("/Views/Shared/baz.cshtml", false))
|
||||
.Returns(Mock.Of<IRazorPage>())
|
||||
.Verifiable();
|
||||
var cache = GetViewLocationCache();
|
||||
|
@ -353,7 +353,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
{
|
||||
// Arrange
|
||||
var pageFactory = new Mock<IRazorPageFactory>(MockBehavior.Strict);
|
||||
pageFactory.Setup(p => p.CreateInstance("some-view-location"))
|
||||
pageFactory.Setup(p => p.CreateInstance("some-view-location", false))
|
||||
.Returns(Mock.Of<IRazorPage>())
|
||||
.Verifiable();
|
||||
var expander = new Mock<IViewLocationExpander>(MockBehavior.Strict);
|
||||
|
@ -384,10 +384,10 @@ namespace Microsoft.AspNet.Mvc.Razor.Test
|
|||
{
|
||||
// Arrange
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("expired-location"))
|
||||
pageFactory.Setup(p => p.CreateInstance("expired-location", false))
|
||||
.Returns((IRazorPage)null)
|
||||
.Verifiable();
|
||||
pageFactory.Setup(p => p.CreateInstance("some-view-location"))
|
||||
pageFactory.Setup(p => p.CreateInstance("some-view-location", false))
|
||||
.Returns(Mock.Of<IRazorPage>())
|
||||
.Verifiable();
|
||||
var cacheMock = new Mock<IViewLocationCache>();
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.PageExecutionInstrumentation;
|
||||
using Microsoft.AspNet.PipelineCore;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
@ -45,7 +47,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider());
|
||||
view.Contextualize(page, isPartial: true);
|
||||
view.Contextualize(page, isPartial: true, pageExecutionListener: null);
|
||||
var viewContext = CreateViewContext(view);
|
||||
var expected = viewContext.Writer;
|
||||
|
||||
|
@ -72,7 +74,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
activator.Object,
|
||||
CreateViewStartProvider());
|
||||
view.Contextualize(page, isPartial: true);
|
||||
view.Contextualize(page, isPartial: true, pageExecutionListener: null);
|
||||
var viewContext = CreateViewContext(view);
|
||||
var expectedWriter = viewContext.Writer;
|
||||
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
|
||||
|
@ -102,7 +104,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
activator.Object,
|
||||
CreateViewStartProvider());
|
||||
view.Contextualize(page, isPartial: true);
|
||||
view.Contextualize(page, isPartial: true, pageExecutionListener: null);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act
|
||||
|
@ -124,15 +126,16 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var view = new RazorView(pageFactory.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
viewStartProvider);
|
||||
view.Contextualize(page, isPartial: true);
|
||||
view.Contextualize(page, isPartial: true, pageExecutionListener: null);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act
|
||||
await view.RenderAsync(viewContext);
|
||||
|
||||
// Assert
|
||||
pageFactory.Verify(v => v.CreateInstance(It.IsAny<string>()), Times.Never());
|
||||
Mock.Get(viewStartProvider).Verify(v => v.GetViewStartPages(It.IsAny<string>()), Times.Never());
|
||||
pageFactory.Verify(v => v.CreateInstance(It.IsAny<string>(), It.IsAny<bool>()), Times.Never());
|
||||
Mock.Get(viewStartProvider)
|
||||
.Verify(v => v.GetViewStartPages(It.IsAny<string>(), It.IsAny<bool>()), Times.Never());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -147,7 +150,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider());
|
||||
view.Contextualize(page, isPartial: false);
|
||||
view.Contextualize(page, isPartial: false, pageExecutionListener: null);
|
||||
var viewContext = CreateViewContext(view);
|
||||
var original = viewContext.Writer;
|
||||
|
||||
|
@ -170,7 +173,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider());
|
||||
view.Contextualize(page, isPartial: false);
|
||||
view.Contextualize(page, isPartial: false, pageExecutionListener: null);
|
||||
var viewContext = CreateViewContext(view);
|
||||
var original = viewContext.Writer;
|
||||
|
||||
|
@ -195,7 +198,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
activator.Object,
|
||||
CreateViewStartProvider());
|
||||
view.Contextualize(page, isPartial: false);
|
||||
view.Contextualize(page, isPartial: false, pageExecutionListener: null);
|
||||
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
|
@ -237,7 +240,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
activator.Object,
|
||||
CreateViewStartProvider(viewStart1, viewStart2));
|
||||
view.Contextualize(page, isPartial: false);
|
||||
view.Contextualize(page, isPartial: false, pageExecutionListener: null);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act
|
||||
|
@ -285,13 +288,13 @@ foot-content";
|
|||
activator.Setup(a => a.Activate(layout, It.IsAny<ViewContext>()))
|
||||
.Verifiable();
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance(LayoutPath))
|
||||
pageFactory.Setup(p => p.CreateInstance(LayoutPath, false))
|
||||
.Returns(layout);
|
||||
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
activator.Object,
|
||||
CreateViewStartProvider());
|
||||
view.Contextualize(page, isPartial: false);
|
||||
view.Contextualize(page, isPartial: false, pageExecutionListener: null);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act
|
||||
|
@ -318,13 +321,13 @@ foot-content";
|
|||
v.RenderBodyPublic();
|
||||
});
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance(LayoutPath))
|
||||
pageFactory.Setup(p => p.CreateInstance(LayoutPath, false))
|
||||
.Returns(layout);
|
||||
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider());
|
||||
view.Contextualize(page, isPartial: false);
|
||||
view.Contextualize(page, isPartial: false, pageExecutionListener: null);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act and Assert
|
||||
|
@ -344,13 +347,13 @@ foot-content";
|
|||
{
|
||||
});
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance(LayoutPath))
|
||||
pageFactory.Setup(p => p.CreateInstance(LayoutPath, false))
|
||||
.Returns(layout);
|
||||
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider());
|
||||
view.Contextualize(page, isPartial: false);
|
||||
view.Contextualize(page, isPartial: false, pageExecutionListener: null);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act and Assert
|
||||
|
@ -396,15 +399,15 @@ body-content";
|
|||
v.RenderBodyPublic();
|
||||
});
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("~/Shared/Layout1.cshtml"))
|
||||
pageFactory.Setup(p => p.CreateInstance("~/Shared/Layout1.cshtml", false))
|
||||
.Returns(layout1);
|
||||
pageFactory.Setup(p => p.CreateInstance("~/Shared/Layout2.cshtml"))
|
||||
pageFactory.Setup(p => p.CreateInstance("~/Shared/Layout2.cshtml", false))
|
||||
.Returns(layout2);
|
||||
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider());
|
||||
view.Contextualize(page, isPartial: false);
|
||||
view.Contextualize(page, isPartial: false, pageExecutionListener: null);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act
|
||||
|
@ -444,13 +447,13 @@ section-content-2";
|
|||
});
|
||||
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("layout-1"))
|
||||
pageFactory.Setup(p => p.CreateInstance("layout-1", false))
|
||||
.Returns(layout1);
|
||||
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider());
|
||||
view.Contextualize(page, isPartial: false);
|
||||
view.Contextualize(page, isPartial: false, pageExecutionListener: null);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act
|
||||
|
@ -488,13 +491,13 @@ section-content-2";
|
|||
});
|
||||
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("layout-1"))
|
||||
pageFactory.Setup(p => p.CreateInstance("layout-1", false))
|
||||
.Returns(layout1);
|
||||
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider());
|
||||
view.Contextualize(page, isPartial: false);
|
||||
view.Contextualize(page, isPartial: false, pageExecutionListener: null);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act
|
||||
|
@ -520,7 +523,7 @@ section-content-2";
|
|||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider());
|
||||
view.Contextualize(page, isPartial: false);
|
||||
view.Contextualize(page, isPartial: false, pageExecutionListener: null);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act and Assert
|
||||
|
@ -555,13 +558,13 @@ section-content-2";
|
|||
v.Layout = "~/Shared/Layout2.cshtml";
|
||||
});
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("~/Shared/Layout1.cshtml"))
|
||||
pageFactory.Setup(p => p.CreateInstance("~/Shared/Layout1.cshtml", false))
|
||||
.Returns(layout1);
|
||||
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
CreateViewStartProvider());
|
||||
view.Contextualize(page, isPartial: false);
|
||||
view.Contextualize(page, isPartial: false, pageExecutionListener: null);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act and Assert
|
||||
|
@ -569,6 +572,153 @@ section-content-2";
|
|||
Assert.Equal(expected, ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RenderAsync_UsesPageExecutionFeatureFromRequest_ToWrapWriter()
|
||||
{
|
||||
// Arrange
|
||||
var pageWriter = CreateBufferedWriter();
|
||||
var layoutWriter = CreateBufferedWriter();
|
||||
|
||||
var layoutExecuted = false;
|
||||
var count = -1;
|
||||
var feature = new Mock<IPageExecutionListenerFeature>(MockBehavior.Strict);
|
||||
feature.Setup(f => f.DecorateWriter(It.IsAny<RazorTextWriter>()))
|
||||
.Returns(() =>
|
||||
{
|
||||
count++;
|
||||
if (count == 0)
|
||||
{
|
||||
return pageWriter;
|
||||
}
|
||||
else if (count == 1)
|
||||
{
|
||||
return layoutWriter;
|
||||
}
|
||||
throw new Exception();
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
var pageContext = Mock.Of<IPageExecutionContext>();
|
||||
feature.Setup(f => f.GetContext("/MyPage.cshtml", pageWriter))
|
||||
.Returns(pageContext)
|
||||
.Verifiable();
|
||||
|
||||
var layoutContext = Mock.Of<IPageExecutionContext>();
|
||||
feature.Setup(f => f.GetContext("/Layout.cshtml", layoutWriter))
|
||||
.Returns(layoutContext)
|
||||
.Verifiable();
|
||||
|
||||
var page = new TestableRazorPage(v =>
|
||||
{
|
||||
v.Layout = "/Layout.cshtml";
|
||||
Assert.Same(pageWriter, v.Output);
|
||||
Assert.Same(pageContext, v.PageExecutionContext);
|
||||
});
|
||||
page.Path = "/MyPage.cshtml";
|
||||
|
||||
var layout = new TestableRazorPage(v =>
|
||||
{
|
||||
Assert.Same(layoutWriter, v.Output);
|
||||
Assert.Same(layoutContext, v.PageExecutionContext);
|
||||
v.RenderBodyPublic();
|
||||
|
||||
layoutExecuted = true;
|
||||
});
|
||||
layout.Path = "/Layout.cshtml";
|
||||
|
||||
var pageFactory = new Mock<IRazorPageFactory>();
|
||||
pageFactory.Setup(p => p.CreateInstance("/Layout.cshtml", true))
|
||||
.Returns(layout);
|
||||
var viewStartProvider = new Mock<IViewStartProvider>();
|
||||
viewStartProvider.Setup(v => v.GetViewStartPages(It.IsAny<string>(), true))
|
||||
.Returns(Enumerable.Empty<IRazorPage>())
|
||||
.Verifiable();
|
||||
var view = new RazorView(pageFactory.Object,
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
viewStartProvider.Object);
|
||||
view.Contextualize(page, isPartial: false, pageExecutionListener: feature.Object);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act
|
||||
await view.RenderAsync(viewContext);
|
||||
|
||||
// Assert
|
||||
feature.Verify();
|
||||
viewStartProvider.Verify();
|
||||
Assert.True(layoutExecuted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RenderAsync_UsesPageExecutionFeatureFromRequest_ToGetExecutionContext()
|
||||
{
|
||||
// Arrange
|
||||
var writer = Mock.Of<TextWriter>();
|
||||
var executed = false;
|
||||
var feature = new Mock<IPageExecutionListenerFeature>(MockBehavior.Strict);
|
||||
|
||||
var pageContext = Mock.Of<IPageExecutionContext>();
|
||||
feature.Setup(f => f.GetContext("/MyPartialPage.cshtml", writer))
|
||||
.Returns(pageContext)
|
||||
.Verifiable();
|
||||
|
||||
var page = new TestableRazorPage(v =>
|
||||
{
|
||||
Assert.Same(writer, v.Output);
|
||||
Assert.Same(pageContext, v.PageExecutionContext);
|
||||
executed = true;
|
||||
});
|
||||
page.Path = "/MyPartialPage.cshtml";
|
||||
|
||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
Mock.Of<IViewStartProvider>());
|
||||
view.Contextualize(page, isPartial: true, pageExecutionListener: feature.Object);
|
||||
var viewContext = CreateViewContext(view);
|
||||
viewContext.Writer = writer;
|
||||
|
||||
// Act
|
||||
await view.RenderAsync(viewContext);
|
||||
|
||||
// Assert
|
||||
feature.Verify();
|
||||
Assert.True(executed);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public async Task RenderAsync_DoesNotSetExecutionContextWhenListenerIsNotRegistered(bool isPartial)
|
||||
{
|
||||
// Arrange
|
||||
var executed = false;
|
||||
var page = new TestableRazorPage(v =>
|
||||
{
|
||||
Assert.Null(v.PageExecutionContext);
|
||||
executed = true;
|
||||
});
|
||||
|
||||
var view = new RazorView(Mock.Of<IRazorPageFactory>(),
|
||||
Mock.Of<IRazorPageActivator>(),
|
||||
Mock.Of<IViewStartProvider>());
|
||||
view.Contextualize(page, isPartial, pageExecutionListener: null);
|
||||
var viewContext = CreateViewContext(view);
|
||||
|
||||
// Act
|
||||
await view.RenderAsync(viewContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(executed);
|
||||
}
|
||||
|
||||
private static TextWriter CreateBufferedWriter()
|
||||
{
|
||||
var mockWriter = new Mock<TextWriter>();
|
||||
var bufferedWriter = mockWriter.As<IBufferedTextWriter>();
|
||||
bufferedWriter.SetupGet(b => b.IsBuffering)
|
||||
.Returns(true);
|
||||
return mockWriter.Object;
|
||||
}
|
||||
|
||||
private static ViewContext CreateViewContext(RazorView view)
|
||||
{
|
||||
var httpContext = new DefaultHttpContext();
|
||||
|
@ -584,7 +734,7 @@ section-content-2";
|
|||
{
|
||||
viewStartPages = viewStartPages ?? new IRazorPage[0];
|
||||
var viewStartProvider = new Mock<IViewStartProvider>();
|
||||
viewStartProvider.Setup(v => v.GetViewStartPages(It.IsAny<string>()))
|
||||
viewStartProvider.Setup(v => v.GetViewStartPages(It.IsAny<string>(), false))
|
||||
.Returns(viewStartPages);
|
||||
|
||||
return viewStartProvider.Object;
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace RazorInstrumentationWebSite
|
||||
{
|
||||
public class HomeController : Controller
|
||||
{
|
||||
public ActionResult FullPath()
|
||||
{
|
||||
return View("/Views/Home/FullPath.cshtml");
|
||||
}
|
||||
|
||||
public ActionResult ViewDiscoveryPath()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="__ToolsVersion__" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>2b2b9876-903c-4065-8d62-2ee832bba106</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Builder;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.PageExecutionInstrumentation;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
|
||||
namespace RazorInstrumentationWebSite
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
var configuration = app.GetTestConfiguration();
|
||||
|
||||
// Set up application services
|
||||
app.UseServices(services =>
|
||||
{
|
||||
// Add MVC services to the services container
|
||||
services.AddMvc(configuration);
|
||||
});
|
||||
|
||||
app.Use(async (HttpContext context, Func<Task> next) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(context.Request.Headers["ENABLE-RAZOR-INSTRUMENTATION"]))
|
||||
{
|
||||
var pageExecutionContext = context.ApplicationServices.GetService<TestPageExecutionContext>();
|
||||
var listenerFeature = new TestPageExecutionListenerFeature(pageExecutionContext);
|
||||
context.SetFeature<IPageExecutionListenerFeature>(listenerFeature);
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
|
||||
// Add MVC to the request pipeline
|
||||
app.UseMvc();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.PageExecutionInstrumentation;
|
||||
|
||||
namespace RazorInstrumentationWebSite
|
||||
{
|
||||
public class TestPageExecutionContext : IPageExecutionContext
|
||||
{
|
||||
public List<Tuple<int, int, bool>> Values { get; }
|
||||
= new List<Tuple<int, int, bool>>();
|
||||
|
||||
public void BeginContext(int position, int length, bool isLiteral)
|
||||
{
|
||||
Values.Add(Tuple.Create(position, length, isLiteral));
|
||||
}
|
||||
|
||||
public void EndContext()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.IO;
|
||||
using Microsoft.AspNet.PageExecutionInstrumentation;
|
||||
|
||||
namespace RazorInstrumentationWebSite
|
||||
{
|
||||
public class TestPageExecutionListenerFeature : IPageExecutionListenerFeature
|
||||
{
|
||||
private readonly IPageExecutionContext _context;
|
||||
|
||||
public TestPageExecutionListenerFeature(IPageExecutionContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public TextWriter DecorateWriter(TextWriter writer)
|
||||
{
|
||||
return writer;
|
||||
}
|
||||
|
||||
public IPageExecutionContext GetContext(string sourceFilePath, TextWriter writer)
|
||||
{
|
||||
return _context;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<p class="@("Hello world")">
|
||||
page-content
|
||||
</p>
|
|
@ -0,0 +1,3 @@
|
|||
<p class="@("Hello world")">
|
||||
page-content
|
||||
</p>
|
|
@ -0,0 +1,4 @@
|
|||
<div>
|
||||
@int.MaxValue
|
||||
@RenderBody()
|
||||
</div>
|
|
@ -0,0 +1,5 @@
|
|||
@{
|
||||
Layout = "/Views/_Layout.cshtml";
|
||||
var viewStartMessage = "viewstart-content";
|
||||
}
|
||||
@viewStartMessage
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.Mvc": "",
|
||||
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
|
||||
"Microsoft.AspNet.Mvc.TestConfiguration": ""
|
||||
},
|
||||
"frameworks": {
|
||||
"aspnet50": { },
|
||||
"aspnetcore50": { }
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче