| File: ViewComponents\ViewViewComponentResult.cs | Web Access |
| Project: src\aspnetcore\src\Mvc\Mvc.ViewFeatures\src\Microsoft.AspNetCore.Mvc.ViewFeatures.csproj (Microsoft.AspNetCore.Mvc.ViewFeatures) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #nullable enable using System.Diagnostics; using System.Globalization; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Mvc.ViewComponents; /// <summary> /// A <see cref="IViewComponentResult"/> that renders a partial view when executed. /// </summary> public class ViewViewComponentResult : IViewComponentResult { // {0} is the component name, {1} is the view name. private const string ViewPathFormat = "Components/{0}/{1}"; private const string DefaultViewName = "Default"; private DiagnosticListener? _diagnosticListener; /// <summary> /// Gets or sets the view name. /// </summary> public string? ViewName { get; set; } /// <summary> /// Gets or sets the <see cref="ViewDataDictionary"/>. /// </summary> public ViewDataDictionary? ViewData { get; set; } /// <summary> /// Gets or sets the <see cref="ITempDataDictionary"/> instance. /// </summary> public ITempDataDictionary TempData { get; set; } = default!; /// <summary> /// Gets or sets the <see cref="ViewEngine"/>. /// </summary> public IViewEngine? ViewEngine { get; set; } /// <summary> /// Locates and renders a view specified by <see cref="ViewName"/>. If <see cref="ViewName"/> is <c>null</c>, /// then the view name searched for is<c>"Default"</c>. /// </summary> /// <param name="context">The <see cref="ViewComponentContext"/> for the current component execution.</param> /// <remarks> /// This method synchronously calls and blocks on <see cref="ExecuteAsync(ViewComponentContext)"/>. /// </remarks> public void Execute(ViewComponentContext context) { ArgumentNullException.ThrowIfNull(context); var task = ExecuteAsync(context); task.GetAwaiter().GetResult(); } /// <summary> /// Locates and renders a view specified by <see cref="ViewName"/>. If <see cref="ViewName"/> is <c>null</c>, /// then the view name searched for is<c>"Default"</c>. /// </summary> /// <param name="context">The <see cref="ViewComponentContext"/> for the current component execution.</param> /// <returns>A <see cref="Task"/> which will complete when view rendering is completed.</returns> public async Task ExecuteAsync(ViewComponentContext context) { ArgumentNullException.ThrowIfNull(context); var viewEngine = ViewEngine ?? ResolveViewEngine(context); var viewContext = context.ViewContext; var isNullOrEmptyViewName = string.IsNullOrEmpty(ViewName); ViewEngineResult? result = null; IEnumerable<string>? originalLocations = null; if (!isNullOrEmptyViewName) { // If view name was passed in is already a path, the view engine will handle this. result = viewEngine.GetView(viewContext.ExecutingFilePath, ViewName!, isMainPage: false); originalLocations = result.SearchedLocations; } if (result == null || !result.Success) { // This will produce a string like: // // Components/Cart/Default // // The view engine will combine this with other path info to search paths like: // // Views/Shared/Components/Cart/Default.cshtml // Views/Home/Components/Cart/Default.cshtml // Areas/Blog/Views/Shared/Components/Cart/Default.cshtml // // This supports a controller or area providing an override for component views. var viewName = isNullOrEmptyViewName ? DefaultViewName : ViewName; var qualifiedViewName = string.Format( CultureInfo.InvariantCulture, ViewPathFormat, context.ViewComponentDescriptor.ShortName, viewName); result = viewEngine.FindView(viewContext, qualifiedViewName, isMainPage: false); } var view = result.EnsureSuccessful(originalLocations).View!; using (view as IDisposable) { if (_diagnosticListener == null) { _diagnosticListener = viewContext.HttpContext.RequestServices.GetRequiredService<DiagnosticListener>(); } _diagnosticListener.ViewComponentBeforeViewExecute(context, view); var childViewContext = new ViewContext( viewContext, view, ViewData ?? context.ViewData, context.Writer); await view.RenderAsync(childViewContext); _diagnosticListener.ViewComponentAfterViewExecute(context, view); } } private static IViewEngine ResolveViewEngine(ViewComponentContext context) { return context.ViewContext.HttpContext.RequestServices.GetRequiredService<ICompositeViewEngine>(); } }