// 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.Globalization; using System.Threading.Tasks; using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.ViewComponents; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.Internal; namespace Microsoft.AspNet.Mvc { /// /// 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}"; /// /// 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([NotNull] ViewComponentContext context) { var task = ExecuteAsync(context); TaskHelper.WaitAndThrowIfFaulted(task); } /// /// 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([NotNull] ViewComponentContext context) { var viewEngine = ViewEngine ?? ResolveViewEngine(context); var viewData = ViewData ?? context.ViewContext.ViewData; string qualifiedViewName; if (ViewName != null && ViewName.Length > 0 && ViewName[0] == '/') { // View name that was passed in is already a rooted path, the view engine will handle this. qualifiedViewName = ViewName; } else { // 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. qualifiedViewName = string.Format( CultureInfo.InvariantCulture, ViewPathFormat, context.ViewComponentDescriptor.ShortName, ViewName ?? "Default"); } var view = FindView(context.ViewContext, viewEngine, qualifiedViewName); var childViewContext = new ViewContext( context.ViewContext, view, ViewData ?? context.ViewContext.ViewData, context.Writer); using (view as IDisposable) { await view.RenderAsync(childViewContext); } } private static IView FindView(ActionContext context, IViewEngine viewEngine, string viewName) { return viewEngine.FindPartialView(context, viewName).EnsureSuccessful().View; } private static IViewEngine ResolveViewEngine(ViewComponentContext context) { return context.ViewContext.HttpContext.RequestServices.GetRequiredService(); } } }