Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@Html.Action in Asp.Net Core

Where is @Html.Action in Asp.net Core? I can see @Html.ActionLink but not a direct call to an Action as before.

Was it replaced by ViewComponents?

like image 223
Francesco Cristallo Avatar asked Nov 13 '14 19:11

Francesco Cristallo


People also ask

How does HTML action work?

This Html. Action renders partial view as an HTML string so we can store it in another string variable. It is string return type method so first it returns result as a string then renders result to response.

What is the difference between HTML action and HTML RenderAction?

The difference between the two is that Html. RenderAction will render the result directly to the Response (which is more efficient if the action returns a large amount of HTML) whereas Html. Action returns a string with the result.

What is asp action in ASP.NET Core?

asp-actionThe asp-action attribute value represents the controller action name included in the generated href attribute. The following markup sets the generated href attribute value to the speaker evaluations page: CSHTML Copy.

What is HTML action link?

Html. ActionLink creates a hyperlink on a view page and the user clicks it to navigate to a new URL. It does not link to a view directly, rather it links to a controller's action.


2 Answers

Yes, ViewComponents would be the new way of doing this, but they are not exactly the same as what @Html.Action was doing before though...for example, in MVC5 and prior, invoking 'child actions' would also execute any filters (for example, if the controller had filters decorated on them) giving them the appearance as regular actions...but this is not true with ViewComponents and they are executed in the context of actual request...

More info on view components: https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components

like image 56
Kiran Avatar answered Sep 18 '22 17:09

Kiran


Update: As of 2.2.2 HttpContextAccessor keep the context in an object (supposedly to prevent inter request mix up) and it impacts the current solution... So you need to provide the following implementation for IHttpContextAccessor (an old version) and register it as a singleton:

public class HttpContextAccessor : IHttpContextAccessor {     private static AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>();     HttpContext IHttpContextAccessor.HttpContext { get => _httpContextCurrent.Value; set => _httpContextCurrent.Value = value; } } 

For asp.net core 2

using Microsoft.AspNetCore.Html; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using System; using System.IO; using System.Threading.Tasks;  namespace Microsoft.AspNetCore.Mvc.Rendering {     public static class HtmlHelperViewExtensions     {         public static IHtmlContent Action(this IHtmlHelper helper, string action, object parameters = null)         {             var controller = (string)helper.ViewContext.RouteData.Values["controller"];              return Action(helper, action, controller, parameters);         }          public static IHtmlContent Action(this IHtmlHelper helper, string action, string controller, object parameters = null)         {             var area = (string)helper.ViewContext.RouteData.Values["area"];              return Action(helper, action, controller, area, parameters);         }          public static IHtmlContent Action(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)         {             if (action == null)                 throw new ArgumentNullException("action");              if (controller == null)                 throw new ArgumentNullException("controller");               var task = RenderActionAsync(helper, action, controller, area, parameters);              return task.Result;         }          private static async Task<IHtmlContent> RenderActionAsync(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)         {             // fetching required services for invocation             var serviceProvider = helper.ViewContext.HttpContext.RequestServices;             var actionContextAccessor = helper.ViewContext.HttpContext.RequestServices.GetRequiredService<IActionContextAccessor>();             var httpContextAccessor = helper.ViewContext.HttpContext.RequestServices.GetRequiredService<IHttpContextAccessor>();             var actionSelector = serviceProvider.GetRequiredService<IActionSelector>();              // creating new action invocation context             var routeData = new RouteData();             foreach (var router in helper.ViewContext.RouteData.Routers)             {                 routeData.PushState(router, null, null);             }             routeData.PushState(null, new RouteValueDictionary(new { controller = controller, action = action, area = area }), null);             routeData.PushState(null, new RouteValueDictionary(parameters ?? new { }), null);              //get the actiondescriptor             RouteContext routeContext = new RouteContext(helper.ViewContext.HttpContext) { RouteData = routeData };             var candidates = actionSelector.SelectCandidates(routeContext);             var actionDescriptor = actionSelector.SelectBestCandidate(routeContext, candidates);              var originalActionContext = actionContextAccessor.ActionContext;             var originalhttpContext = httpContextAccessor.HttpContext;             try             {                 var newHttpContext = serviceProvider.GetRequiredService<IHttpContextFactory>().Create(helper.ViewContext.HttpContext.Features);                 if (newHttpContext.Items.ContainsKey(typeof(IUrlHelper)))                 {                     newHttpContext.Items.Remove(typeof(IUrlHelper));                 }                 newHttpContext.Response.Body = new MemoryStream();                 var actionContext = new ActionContext(newHttpContext, routeData, actionDescriptor);                 actionContextAccessor.ActionContext = actionContext;                 var invoker = serviceProvider.GetRequiredService<IActionInvokerFactory>().CreateInvoker(actionContext);                 await invoker.InvokeAsync();                 newHttpContext.Response.Body.Position = 0;                 using (var reader = new StreamReader(newHttpContext.Response.Body))                 {                     return new HtmlString(reader.ReadToEnd());                 }             }             catch (Exception ex)             {                 return new HtmlString(ex.Message);             }             finally             {                 actionContextAccessor.ActionContext = originalActionContext;                 httpContextAccessor.HttpContext = originalhttpContext;                 if (helper.ViewContext.HttpContext.Items.ContainsKey(typeof(IUrlHelper)))                 {                     helper.ViewContext.HttpContext.Items.Remove(typeof(IUrlHelper));                 }             }         }     } } 

It is based on Aries response. I corrected what wasn't compiling for 2.0 and I added a couple of tweaks. There are 2 glorified static values for the current httpcontext and the current actioncontext. The one for httpcontext is set in IHttpContextFactory.Create and I set the one for actioncontext in the code. Note that depending on the features you use IActionContextAccessor and IHttpContextAccessor may not be registered by default, so you may need to add them in your startup:

services.AddSingleton<IActionContextAccessor, ActionContextAccessor>(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

HttpContext is just a wrapper around HttpContext.Features, so if you change something in one, it also changes in the other... I reset what I know about in the finally of the try/catch.

I removed the IUrlHelper from the Items cache since this value will be reused even if the actionContext to build the urlHelper is different(IUrlHelperFactory.GetUrlHelper).

Asp.net core 2.0 assumes you won't do this, there is a good chance there are other cached things, so I recommend to be careful when using this and just don't if you don't need to.

like image 30
Yepeekai Avatar answered Sep 18 '22 17:09

Yepeekai