// 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.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; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNet.Mvc.ViewComponents { /// /// A that renders a partial view when executed. /// 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 DiagnosticSource _diagnosticSource; /// /// Gets or sets the view name. /// public string ViewName { get; set; } /// /// Gets or sets the . /// public ViewDataDictionary ViewData { get; set; } /// /// Gets or sets the instance. /// public ITempDataDictionary TempData { get; set; } /// /// Gets or sets the . /// public IViewEngine ViewEngine { get; set; } /// /// Locates and renders a view specified by . If is null, /// then the view name searched for is"Default". /// /// The for the current component execution. /// /// This method synchronously calls and blocks on . /// public void Execute(ViewComponentContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var task = ExecuteAsync(context); task.GetAwaiter().GetResult(); } /// /// Locates and renders a view specified by . If is null, /// then the view name searched for is"Default". /// /// The for the current component execution. /// A which will complete when view rendering is completed. public async Task ExecuteAsync(ViewComponentContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var viewContext = context.ViewContext; var viewData = ViewData ?? context.ViewData; var viewEngine = ViewEngine ?? ResolveViewEngine(context); var isNullOrEmptyViewName = string.IsNullOrEmpty(ViewName); ViewEngineResult result = null; IEnumerable 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, isPartial: true); 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, isPartial: true); } var view = result.EnsureSuccessful(originalLocations).View; using (view as IDisposable) { if (_diagnosticSource == null) { _diagnosticSource = viewContext.HttpContext.RequestServices.GetRequiredService(); } _diagnosticSource.ViewComponentBeforeViewExecute(context, view); var childViewContext = new ViewContext( viewContext, view, ViewData ?? context.ViewData, context.Writer); await view.RenderAsync(childViewContext); _diagnosticSource.ViewComponentAfterViewExecute(context, view); } } private static IViewEngine ResolveViewEngine(ViewComponentContext context) { return context.ViewContext.HttpContext.RequestServices.GetRequiredService(); } } }