[Fixes #3274] Added DiagnosticsSource for ViewComponent

This commit is contained in:
Ajay Bhargav Baaskaran 2015-10-19 16:36:28 -07:00
parent bcde82cf62
commit 624c918de5
12 changed files with 464 additions and 51 deletions

View File

@ -0,0 +1,87 @@
// 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.Diagnostics;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.ViewComponents;
using Microsoft.AspNet.Mvc.ViewEngines;
namespace Microsoft.AspNet.Mvc.Diagnostics
{
public static class ViewComponentDiagnosticSourceExtensions
{
public static void BeforeViewComponent(
this DiagnosticSource diagnosticSource,
ViewComponentContext context,
object viewComponent)
{
if (diagnosticSource.IsEnabled("Microsoft.AspNet.Mvc.BeforeViewComponent"))
{
diagnosticSource.Write(
"Microsoft.AspNet.Mvc.BeforeViewComponent",
new
{
actionDescriptor = context.ViewContext.ActionDescriptor,
viewComponentContext = context,
viewComponent = viewComponent
});
}
}
public static void AfterViewComponent(
this DiagnosticSource diagnosticSource,
ViewComponentContext context,
IViewComponentResult result,
object viewComponent)
{
if (diagnosticSource.IsEnabled("Microsoft.AspNet.Mvc.AfterViewComponent"))
{
diagnosticSource.Write(
"Microsoft.AspNet.Mvc.AfterViewComponent",
new
{
actionDescriptor = context.ViewContext.ActionDescriptor,
viewComponentContext = context,
viewComponentResult = result,
viewComponent = viewComponent
});
}
}
public static void ViewComponentBeforeViewExecute(
this DiagnosticSource diagnosticSource,
ViewComponentContext context,
IView view)
{
if (diagnosticSource.IsEnabled("Microsoft.AspNet.Mvc.ViewComponentBeforeViewExecute"))
{
diagnosticSource.Write(
"Microsoft.AspNet.Mvc.ViewComponentBeforeViewExecute",
new
{
actionDescriptor = context.ViewContext.ActionDescriptor,
viewComponentContext = context,
view = view
});
}
}
public static void ViewComponentAfterViewExecute(
this DiagnosticSource diagnosticSource,
ViewComponentContext context,
IView view)
{
if (diagnosticSource.IsEnabled("Microsoft.AspNet.Mvc.ViewComponentAfterViewExecute"))
{
diagnosticSource.Write(
"Microsoft.AspNet.Mvc.ViewComponentAfterViewExecute",
new
{
actionDescriptor = context.ViewContext.ActionDescriptor,
viewComponentContext = context,
view = view
});
}
}
}
}

View File

@ -0,0 +1,85 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewEngines;
namespace Microsoft.AspNet.Mvc.Diagnostics
{
public static class ViewExecutorDiagnosticSourceExtensions
{
public static void BeforeView(
this DiagnosticSource diagnosticSource,
IView view,
ViewContext viewContext)
{
if (diagnosticSource.IsEnabled("Microsoft.AspNet.Mvc.BeforeView"))
{
diagnosticSource.Write(
"Microsoft.AspNet.Mvc.BeforeView",
new { view = view, viewContext = viewContext, });
}
}
public static void AfterView(
this DiagnosticSource diagnosticSource,
IView view,
ViewContext viewContext)
{
if (diagnosticSource.IsEnabled("Microsoft.AspNet.Mvc.AfterView"))
{
diagnosticSource.Write(
"Microsoft.AspNet.Mvc.AfterView",
new { view = view, viewContext = viewContext, });
}
}
public static void ViewFound(
this DiagnosticSource diagnosticSource,
ActionContext actionContext,
bool isPartial,
PartialViewResult viewResult,
string viewName,
IView view)
{
if (diagnosticSource.IsEnabled("Microsoft.AspNet.Mvc.ViewFound"))
{
diagnosticSource.Write(
"Microsoft.AspNet.Mvc.ViewFound",
new
{
actionContext = actionContext,
isPartial = isPartial,
result = viewResult,
viewName = viewName,
view = view,
});
}
}
public static void ViewNotFound(
this DiagnosticSource diagnosticSource,
ActionContext actionContext,
bool isPartial,
PartialViewResult viewResult,
string viewName,
IEnumerable<string> searchedLocations)
{
if (diagnosticSource.IsEnabled("Microsoft.AspNet.Mvc.ViewNotFound"))
{
diagnosticSource.Write(
"Microsoft.AspNet.Mvc.ViewNotFound",
new
{
actionContext = actionContext,
isPartial = isPartial,
result = viewResult,
viewName = viewName,
searchedLocations = searchedLocations,
});
}
}
}
}

View File

@ -2,10 +2,12 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Controllers;
using Microsoft.AspNet.Mvc.Diagnostics;
using Microsoft.AspNet.Mvc.Infrastructure;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewFeatures;
@ -16,10 +18,12 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
{
private readonly ITypeActivatorCache _typeActivatorCache;
private readonly IViewComponentActivator _viewComponentActivator;
private readonly DiagnosticSource _diagnosticSource;
public DefaultViewComponentInvoker(
ITypeActivatorCache typeActivatorCache,
IViewComponentActivator viewComponentActivator)
IViewComponentActivator viewComponentActivator,
DiagnosticSource diagnosticSource)
{
if (typeActivatorCache == null)
{
@ -31,8 +35,14 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
throw new ArgumentNullException(nameof(viewComponentActivator));
}
if (diagnosticSource == null)
{
throw new ArgumentNullException(nameof(diagnosticSource));
}
_typeActivatorCache = typeActivatorCache;
_viewComponentActivator = viewComponentActivator;
_diagnosticSource = diagnosticSource;
}
public void Invoke(ViewComponentContext context)
@ -52,6 +62,7 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
}
var result = InvokeSyncCore(method, context);
result.Execute(context);
}
@ -124,9 +135,15 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
var component = CreateComponent(context);
_diagnosticSource.BeforeViewComponent(context, component);
var result = await ControllerActionExecutor.ExecuteAsync(method, component, context.Arguments);
return CoerceToViewComponentResult(result);
var viewComponentResult = CoerceToViewComponentResult(result);
_diagnosticSource.AfterViewComponent(context, viewComponentResult, component);
return viewComponentResult;
}
public IViewComponentResult InvokeSyncCore(MethodInfo method, ViewComponentContext context)
@ -145,6 +162,8 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
object result = null;
_diagnosticSource.BeforeViewComponent(context, component);
try
{
result = method.Invoke(component, context.Arguments);
@ -156,7 +175,11 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
exceptionInfo.Throw();
}
return CoerceToViewComponentResult(result);
var viewComponentResult = CoerceToViewComponentResult(result);
_diagnosticSource.AfterViewComponent(context, viewComponentResult, component);
return viewComponentResult;
}
private static IViewComponentResult CoerceToViewComponentResult(object value)

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using Microsoft.AspNet.Mvc.Infrastructure;
namespace Microsoft.AspNet.Mvc.ViewComponents
@ -10,13 +11,16 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
{
private readonly ITypeActivatorCache _typeActivatorCache;
private readonly IViewComponentActivator _viewComponentActivator;
private readonly DiagnosticSource _diagnosticSource;
public DefaultViewComponentInvokerFactory(
ITypeActivatorCache typeActivatorCache,
IViewComponentActivator viewComponentActivator)
IViewComponentActivator viewComponentActivator,
DiagnosticSource diagnosticSource)
{
_typeActivatorCache = typeActivatorCache;
_viewComponentActivator = viewComponentActivator;
_diagnosticSource = diagnosticSource;
}
/// <inheritdoc />
@ -32,7 +36,8 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
return new DefaultViewComponentInvoker(
_typeActivatorCache,
_viewComponentActivator);
_viewComponentActivator,
_diagnosticSource);
}
}
}

View File

@ -2,8 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Diagnostics;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewEngines;
using Microsoft.AspNet.Mvc.ViewFeatures;
@ -20,6 +22,8 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
private const string ViewPathFormat = "Components/{0}/{1}";
private const string DefaultViewName = "Default";
private DiagnosticSource _diagnosticSource;
/// <summary>
/// Gets or sets the view name.
/// </summary>
@ -115,7 +119,16 @@ namespace Microsoft.AspNet.Mvc.ViewComponents
using (view as IDisposable)
{
if (_diagnosticSource == null)
{
_diagnosticSource = context.ViewContext.HttpContext.RequestServices.GetRequiredService<DiagnosticSource>();
}
_diagnosticSource.ViewComponentBeforeViewExecute(context, view);
await view.RenderAsync(childViewContext);
_diagnosticSource.ViewComponentAfterViewExecute(context, view);
}
}

View File

@ -4,6 +4,7 @@
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Diagnostics;
using Microsoft.AspNet.Mvc.Infrastructure;
using Microsoft.AspNet.Mvc.Logging;
using Microsoft.AspNet.Mvc.ViewEngines;
@ -70,37 +71,13 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
var result = viewEngine.FindPartialView(actionContext, viewName);
if (result.Success)
{
if (DiagnosticSource.IsEnabled("Microsoft.AspNet.Mvc.ViewFound"))
{
DiagnosticSource.Write(
"Microsoft.AspNet.Mvc.ViewFound",
new
{
actionContext = actionContext,
isPartial = true,
result = viewResult,
viewName = viewName,
view = result.View,
});
}
DiagnosticSource.ViewFound(actionContext, true, viewResult, viewName, result.View);
Logger.LogVerbose("The partial view '{PartialViewName}' was found.", viewName);
}
else
{
if (DiagnosticSource.IsEnabled("Microsoft.AspNet.Mvc.ViewNotFound"))
{
DiagnosticSource.Write(
"Microsoft.AspNet.Mvc.ViewNotFound",
new
{
actionContext = actionContext,
isPartial = true,
result = viewResult,
viewName = viewName,
searchedLocations = result.SearchedLocations
});
}
DiagnosticSource.ViewNotFound(actionContext, true, viewResult, viewName, result.SearchedLocations);
Logger.LogError(
"The partial view '{PartialViewName}' was not found. Searched locations: {SearchedViewLocations}",

View File

@ -5,6 +5,7 @@ using System;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Diagnostics;
using Microsoft.AspNet.Mvc.Infrastructure;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering;
@ -162,21 +163,11 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
writer,
ViewOptions.HtmlHelperOptions);
if (DiagnosticSource.IsEnabled("Microsoft.AspNet.Mvc.BeforeView"))
{
DiagnosticSource.Write(
"Microsoft.AspNet.Mvc.BeforeView",
new { view = view, viewContext = viewContext, });
}
DiagnosticSource.BeforeView(view, viewContext);
await view.RenderAsync(viewContext);
if (DiagnosticSource.IsEnabled("Microsoft.AspNet.Mvc.AfterView"))
{
DiagnosticSource.Write(
"Microsoft.AspNet.Mvc.AfterView",
new { view = view, viewContext = viewContext, });
}
DiagnosticSource.AfterView(view, viewContext);
// Perf: Invoke FlushAsync to ensure any buffered content is asynchronously written to the underlying
// response asynchronously. In the absence of this line, the buffer gets synchronously written to the

View File

@ -0,0 +1,9 @@
// 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
{
public interface IProxyViewComponentContext
{
}
}

View File

@ -0,0 +1,9 @@
// 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
{
public interface IProxyViewComponentResult
{
}
}

View File

@ -220,5 +220,109 @@ namespace Microsoft.AspNet.Mvc
ViewContext = viewContext,
};
}
public class OnBeforeViewComponentEventData
{
public IProxyActionDescriptor ActionDescriptor { get; set; }
public IProxyViewComponentContext ViewComponentContext { get; set; }
public object ViewComponent { get; set; }
}
public OnBeforeViewComponentEventData BeforeViewComponent { get; set; }
[DiagnosticName("Microsoft.AspNet.Mvc.BeforeViewComponent")]
public virtual void OnBeforeViewComponent(
IProxyActionDescriptor actionDescriptor,
IProxyViewComponentContext viewComponentContext,
object viewComponent)
{
BeforeViewComponent = new OnBeforeViewComponentEventData()
{
ActionDescriptor = actionDescriptor,
ViewComponentContext = viewComponentContext,
ViewComponent = viewComponent
};
}
public class OnAfterViewComponentEventData
{
public IProxyActionDescriptor ActionDescriptor { get; set; }
public IProxyViewComponentContext ViewComponentContext { get; set; }
public IProxyViewComponentResult ViewComponentResult { get; set; }
public object ViewComponent { get; set; }
}
public OnAfterViewComponentEventData AfterViewComponent { get; set; }
[DiagnosticName("Microsoft.AspNet.Mvc.AfterViewComponent")]
public virtual void OnAfterViewComponent(
IProxyActionDescriptor actionDescriptor,
IProxyViewComponentContext viewComponentContext,
IProxyViewComponentResult viewComponentResult,
object viewComponent)
{
AfterViewComponent = new OnAfterViewComponentEventData()
{
ActionDescriptor = actionDescriptor,
ViewComponentContext = viewComponentContext,
ViewComponentResult = viewComponentResult,
ViewComponent = viewComponent
};
}
public class OnViewComponentBeforeViewExecuteEventData
{
public IProxyActionDescriptor ActionDescriptor { get; set; }
public IProxyViewComponentContext ViewComponentContext { get; set; }
public IProxyView View { get; set; }
}
public OnViewComponentBeforeViewExecuteEventData ViewComponentBeforeViewExecute { get; set; }
[DiagnosticName("Microsoft.AspNet.Mvc.ViewComponentBeforeViewExecute")]
public virtual void OnViewComponentBeforeViewExecute(
IProxyActionDescriptor actionDescriptor,
IProxyViewComponentContext viewComponentContext,
IProxyView view)
{
ViewComponentBeforeViewExecute = new OnViewComponentBeforeViewExecuteEventData()
{
ActionDescriptor = actionDescriptor,
ViewComponentContext = viewComponentContext,
View = view
};
}
public class OnViewComponentAfterViewExecuteEventData
{
public IProxyActionDescriptor ActionDescriptor { get; set; }
public IProxyViewComponentContext ViewComponentContext { get; set; }
public IProxyView View { get; set; }
}
public OnViewComponentAfterViewExecuteEventData ViewComponentAfterViewExecute { get; set; }
[DiagnosticName("Microsoft.AspNet.Mvc.ViewComponentAfterViewExecute")]
public virtual void OnViewComponentAfterViewExecute(
IProxyActionDescriptor actionDescriptor,
IProxyViewComponentContext viewComponentContext,
IProxyView view)
{
ViewComponentAfterViewExecute = new OnViewComponentAfterViewExecuteEventData()
{
ActionDescriptor = actionDescriptor,
ViewComponentContext = viewComponentContext,
View = view
};
}
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading.Tasks;
@ -103,7 +104,7 @@ namespace Microsoft.AspNet.Mvc
var expected = $"A view component named '{typeof(TextViewComponent).FullName}' could not be found.";
var actionContext = CreateActionContext();
var services = CreateServices(actionContext.HttpContext);
var services = CreateServices(diagnosticListener: null, context: actionContext.HttpContext);
services.AddSingleton<IViewComponentSelector>();
@ -175,6 +176,44 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal("Hello-Async, World!", body);
}
[Fact]
public async Task ExecuteResultAsync_ExecutesViewComponent_AndWritesDiagnosticSource()
{
// Arrange
var descriptor = new ViewComponentDescriptor()
{
FullName = "Full.Name.Text",
ShortName = "Text",
Type = typeof(TextViewComponent),
};
var adapter = new TestDiagnosticListener();
var actionContext = CreateActionContext(adapter, descriptor);
var viewComponentResult = new ViewComponentResult()
{
Arguments = new object[] { "World!" },
ViewComponentName = "Text",
TempData = _tempDataDictionary,
};
// Act
await viewComponentResult.ExecuteResultAsync(actionContext);
// Assert
var body = ReadBody(actionContext.HttpContext.Response);
Assert.Equal("Hello, World!", body);
Assert.NotNull(adapter.BeforeViewComponent?.ActionDescriptor);
Assert.NotNull(adapter.BeforeViewComponent?.ViewComponentContext);
Assert.NotNull(adapter.BeforeViewComponent?.ViewComponent);
Assert.NotNull(adapter.AfterViewComponent?.ActionDescriptor);
Assert.NotNull(adapter.AfterViewComponent?.ViewComponentContext);
Assert.NotNull(adapter.AfterViewComponent?.ViewComponentResult);
Assert.NotNull(adapter.AfterViewComponent?.ViewComponent);
}
[Fact]
public async Task ExecuteResultAsync_ExecutesViewComponent_ByShortName()
{
@ -413,12 +452,18 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal(expectedContentType, actionContext.HttpContext.Response.ContentType);
}
private IServiceCollection CreateServices(HttpContext context, params ViewComponentDescriptor[] descriptors)
private IServiceCollection CreateServices(object diagnosticListener, HttpContext context, params ViewComponentDescriptor[] descriptors)
{
var httpContext = new HttpContextAccessor() { HttpContext = context };
var tempDataProvider = new SessionStateTempDataProvider();
var diagnosticSource = new DiagnosticListener("Microsoft.AspNet");
if (diagnosticListener != null)
{
diagnosticSource.SubscribeWithAdapter(diagnosticListener);
}
var services = new ServiceCollection();
services.AddInstance<DiagnosticSource>(diagnosticSource);
services.AddSingleton<IOptions<MvcViewOptions>, TestOptionsManager<MvcViewOptions>>();
services.AddTransient<IViewComponentHelper, DefaultViewComponentHelper>();
services.AddSingleton<IViewComponentSelector, DefaultViewComponentSelector>();
@ -435,10 +480,10 @@ namespace Microsoft.AspNet.Mvc
return services;
}
private HttpContext CreateHttpContext(params ViewComponentDescriptor[] descriptors)
private HttpContext CreateHttpContext(object diagnosticListener, params ViewComponentDescriptor[] descriptors)
{
var httpContext = new DefaultHttpContext();
var services = CreateServices(httpContext, descriptors);
var services = CreateServices(diagnosticListener, httpContext, descriptors);
httpContext.Response.Body = new MemoryStream();
httpContext.RequestServices = services.BuildServiceProvider();
@ -446,11 +491,17 @@ namespace Microsoft.AspNet.Mvc
return httpContext;
}
private ActionContext CreateActionContext(object diagnosticListener, params ViewComponentDescriptor[] descriptors)
{
return new ActionContext(CreateHttpContext(diagnosticListener, descriptors), new RouteData(), new ActionDescriptor());
}
private ActionContext CreateActionContext(params ViewComponentDescriptor[] descriptors)
{
return new ActionContext(CreateHttpContext(descriptors), new RouteData(), new ActionDescriptor());
return CreateActionContext(null, descriptors);
}
private class FixedSetViewComponentDescriptorProvider : IViewComponentDescriptorProvider
{
private readonly ViewComponentDescriptor[] _descriptors;

View File

@ -3,6 +3,7 @@
#if MOCK_SUPPORT
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Internal;
@ -91,6 +92,48 @@ namespace Microsoft.AspNet.Mvc
view.Verify();
}
[Fact]
public void Execute_ResolvesView_AndWritesDiagnosticSource()
{
// Arrange
var view = new Mock<IView>(MockBehavior.Strict);
view.Setup(v => v.RenderAsync(It.IsAny<ViewContext>()))
.Returns(Task.FromResult(result: true))
.Verifiable();
var viewEngine = new Mock<IViewEngine>(MockBehavior.Strict);
viewEngine.Setup(e => e.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>()))
.Returns(ViewEngineResult.Found("Default", view.Object))
.Verifiable();
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
var result = new ViewViewComponentResult
{
ViewEngine = viewEngine.Object,
ViewData = viewData,
TempData = _tempDataDictionary,
};
var adapter = new TestDiagnosticListener();
var viewComponentContext = GetViewComponentContext(view.Object, viewData, adapter);
// Act
result.Execute(viewComponentContext);
// Assert
viewEngine.Verify();
view.Verify();
Assert.NotNull(adapter.ViewComponentBeforeViewExecute?.ActionDescriptor);
Assert.NotNull(adapter.ViewComponentBeforeViewExecute?.ViewComponentContext);
Assert.NotNull(adapter.ViewComponentBeforeViewExecute?.View);
Assert.NotNull(adapter.ViewComponentAfterViewExecute?.ActionDescriptor);
Assert.NotNull(adapter.ViewComponentAfterViewExecute?.ViewComponentContext);
Assert.NotNull(adapter.ViewComponentAfterViewExecute?.View);
}
[Fact]
public void Execute_ThrowsIfPartialViewCannotBeFound()
{
@ -204,6 +247,8 @@ namespace Microsoft.AspNet.Mvc
var serviceProvider = new Mock<IServiceProvider>();
serviceProvider.Setup(p => p.GetService(typeof(ICompositeViewEngine)))
.Returns(viewEngine.Object);
serviceProvider.Setup(p => p.GetService(typeof(DiagnosticSource)))
.Returns(new DiagnosticListener("Test"));
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
@ -345,9 +390,23 @@ namespace Microsoft.AspNet.Mvc
viewEngine.Verify();
}
private static ViewComponentContext GetViewComponentContext(IView view, ViewDataDictionary viewData)
private static ViewComponentContext GetViewComponentContext(IView view, ViewDataDictionary viewData, object diagnosticListener = null)
{
var actionContext = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor());
var diagnosticSource = new DiagnosticListener("Microsoft.AspNet");
if (diagnosticListener == null)
{
diagnosticListener = new TestDiagnosticListener();
}
diagnosticSource.SubscribeWithAdapter(diagnosticListener);
var serviceProvider = new Mock<IServiceProvider>();
serviceProvider.Setup(s => s.GetService(typeof(DiagnosticSource))).Returns(diagnosticSource);
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = serviceProvider.Object;
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var viewContext = new ViewContext(
actionContext,
view,