66

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?

ArunPratap
  • 4,816
  • 7
  • 25
  • 43
Francesco Cristallo
  • 2,856
  • 5
  • 28
  • 57

11 Answers11

32

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://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components

CodingYourLife
  • 7,172
  • 5
  • 55
  • 69
Kiran
  • 56,921
  • 15
  • 176
  • 161
  • 1
    Excellent. Now we can use asynchronous calls when invoking "partial views" (now ViewComponents) – Jose A May 24 '15 at 12:05
28

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.

Yepeekai
  • 2,545
  • 29
  • 22
  • 4
    Make sure you include `services.AddSingleton();` in your Startup.cs as 2.0 does not do this by default. – Jordan B Dec 20 '18 at 20:38
  • 2
    good point! I happened to already have it indirectly via kendo – Yepeekai Dec 20 '18 at 21:02
  • 2
    With 2.1 you will need to register HttpContextAccessor. Add `services.AddHttpContextAccessor();` or `services.AddSingleton();` in your Startup.cs. – Dustin Townsend Mar 29 '19 at 13:46
  • This does not render the view after HTTP POST, any pointers appreciated – Nitin Sawant Feb 27 '20 at 11:07
  • Also the response body stream is disposed after using this, I've got a middleware that makes changes to the body before the end of the request and it fails after using this extension ;( – kurdemol94 Mar 19 '20 at 15:40
  • As I understood, we should add "using (newHttpContext.Response.Body = new MemoryStream())" – Pavel Samoylenko Dec 17 '20 at 09:24
  • @Yepeekai any idea whether it'll work in ASP.NET 6/7? – pzaj Dec 12 '22 at 11:46
  • @pzaj, we were having problems at every asp.net update, so we eventually replaced Html.Action with partial views and we converted controller actions to static methods to create/fill the model to feed those partial views. – Yepeekai Dec 12 '22 at 18:47
  • @Yepeekai, I was trying to use your code reference, It works for .NET 2.1 not for .NET 6.0+, actually it builds the problem is original HttpContext values become null for _httpContextAccessor.HttpContext?.Items[key] Whatever values I stored earlier after calling the RenderActionAsync method, did you or anyone see this problem? – Velmurugan Apr 20 '23 at 15:06
  • Is that solution works for .NET 6+? – Velmurugan Apr 20 '23 at 15:21
18

ViewComponents are great, but not so great for Ajax.

If you really miss the @Html.RenderAction method, then here's a quick implementation I threw together for AspNetCore.

using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Routing;

namespace Microsoft.AspNetCore.Mvc.Rendering    {

    public static class HtmlHelperViewExtensions
    {

        public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, object parameters = null)
        {
            var controller =  (string)helper.ViewContext.RouteData.Values["controller"];

            return RenderAction(helper, action, controller, parameters);
        }

        public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, string controller, object parameters = null)
        {
            var area = (string)helper.ViewContext.RouteData.Values["area"];

            return RenderAction(helper, action, controller, area, parameters);
        }

        public static IHtmlContent RenderAction(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");

            if (area == null)
                throw new ArgumentNullException("area");

            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 currentHttpContext = helper.ViewContext?.HttpContext;
            var httpContextFactory = GetServiceOrFail<IHttpContextFactory>(currentHttpContext);
            var actionInvokerFactory = GetServiceOrFail<IActionInvokerFactory>(currentHttpContext);
            var actionSelector = GetServiceOrFail<IActionSelectorDecisionTreeProvider>(currentHttpContext);

            // creating new action invocation context
            var routeData = new RouteData();
            var routeParams = new RouteValueDictionary(parameters ?? new { });
            var routeValues = new RouteValueDictionary(new { area = area, controller = controller, action = action });
            var newHttpContext = httpContextFactory.Create(currentHttpContext.Features);

            newHttpContext.Response.Body = new MemoryStream();

            foreach (var router in helper.ViewContext.RouteData.Routers)
                routeData.PushState(router, null, null);

            routeData.PushState(null, routeValues, null);
            routeData.PushState(null, routeParams, null);

            var actionDescriptor = actionSelector.DecisionTree.Select(routeValues).First();
            var actionContext = new ActionContext(newHttpContext, routeData, actionDescriptor);

            // invoke action and retreive the response body
            var invoker = actionInvokerFactory.CreateInvoker(actionContext);
            string content = null;

            await invoker.InvokeAsync().ContinueWith(task => {
                if (task.IsFaulted)
                {
                    content = task.Exception.Message;
                }
                else if (task.IsCompleted)
                {
                    newHttpContext.Response.Body.Position = 0;
                    using (var reader = new StreamReader(newHttpContext.Response.Body))
                        content = reader.ReadToEnd();
                }
            });

            return new HtmlString(content);
        }

        private static TService GetServiceOrFail<TService>(HttpContext httpContext)
        {
            if (httpContext == null)
                throw new ArgumentNullException(nameof(httpContext));

            var service = httpContext.RequestServices.GetService(typeof(TService));

            if (service == null)
                throw new InvalidOperationException($"Could not locate service: {nameof(TService)}");

            return (TService)service;
        }
    }
}

You can invoke from your view using one of these methods:

@Html.RenderAction("action", "controller", "area", new { id = 1})
@Html.RenderAction("action", "controller", new { id = 1})
@Html.RenderAction("action", new { id = 1})

Note:

Controller name, and optionally area name, will default to the corresponding values from the ActionContext if not provided.

Aries
  • 219
  • 2
  • 2
  • 1
    Technically, this code is a replacement for Html.Action(), which returns the action as a string value. Html.RenderAction() writes the action directly to the response stream, and returns void. – ReflexiveCode Jan 30 '17 at 03:40
  • I've tried to use this code, but when I call Html.RenderAction more than once I get an error saying "One or more errors occurred. (An item with the same key has already been added. Key: System.Object)". It happens in ControllerActionInvoker.Next which finally ends up in "System.ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException" Do you have any idea what object is used as a key and how I could change the code to allow for multiple uses in the same request? – Dan Pettersson Feb 04 '17 at 17:23
  • Having the same error and can't find a way to solve it :(. One or more errors occurred. (An item with the same key has already been added. Key: System.Object) – brechtvhb Feb 08 '17 at 11:00
  • I solved it. Change this in startup.cs: services.AddMvc(); to services.AddMvc(opts => opts.Filters.RemoveAt(1)); It removes the filter "TempDataFilter". I don't know what it used for but it solved my issue and I haven't had any other issues comming up by disabling it. – brechtvhb Feb 09 '17 at 09:12
  • I've tried to use this code too, but when I have two action with same name (one for get e other for post) the actionInvoker choose the Post version of the action. How to force the Get version? – Fernando Mondo May 15 '17 at 14:07
  • Workaround not working on dotnet core 2.0 here is a link https://github.com/Microsoft/aspnet-api-versioning/issues/154 – IdontCareAboutReputationPoints Oct 18 '17 at 14:15
  • Is that solution works for .NET 6+? – Velmurugan Apr 20 '23 at 15:22
8

@Html.Action was replaced by ViewComponents. I dislike ViewComponents for multiple reasons.

However I am using alternative pattern to @Html.Action

First I create Action on controller that is returning partial view with a content that I want to display in page i.e.

    [HttpGet]
    public async Task<IActionResult> GetFoo()
    {
        return PartialView("_Foo", new Foo());
    }

Then I place div on page where the foo view should be loaded and include IIFE at the bottom of that page. I.e. code bellow will load GetFoo view and then insert that html to div with id foo-view.

<div class="page">
    <div id="foo-view" data-url="@Url.Action(action: "GetFoo", controller: "Home")"></div>
</div>

<script>
    $(document).ready(function () {
        (function () {
            var url = $("#foo-view").data("url");
            $("#foo-view").load(url);
        })();
    });
</script>

You may also want to display spinner while view is beign fetched from server.

Ľuboš Pilka
  • 1,044
  • 1
  • 11
  • 17
6

For Net Core 2.0

using Microsoft.AspNetCore.Mvc.Infrastructure;

replace

// var actionSelector = GetServiceOrFail<IActionSelectorDecisionTreeProvider>(currentHttpContext); 
var actionSelector = GetServiceOrFail<IActionDescriptorCollectionProvider>(currentHttpContext); 

and

// var actionDescriptor = actionSelector.DecisionTree.Select(routeValues).First(); 
var actionDescriptor = actionSelector.ActionDescriptors.Items.Where(i => i.RouteValues["Controller"] == controller && i.RouteValues["Action"] == action).First();
Jax
  • 93
  • 1
  • 2
5

I used the codes of people on this page to get the correct result.

https://stackoverflow.com/a/39951006/6778726

https://stackoverflow.com/a/46859170/6778726

For example, in the old class, when executing the following code, an error was displayed

@Html.RenderAction("About", "Home")

The following code has been fixed:

using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;

namespace Microsoft.AspNetCore.Mvc.Rendering
{

    public static class HtmlHelperViewExtensions
    {
        public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, object parameters = null)
        {
            var controller = (string)helper.ViewContext.RouteData.Values["controller"];
            return RenderAction(helper, action, controller, parameters);
        }

        public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, string controller, object parameters = null)
        {
            var area = (string)helper.ViewContext.RouteData.Values["area"];
            return RenderAction(helper, action, controller, area, parameters);
        }

        public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
        {
            if (action == null)
                throw new ArgumentNullException(nameof(controller));
            if (controller == null)
                throw new ArgumentNullException(nameof(action));

            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 currentHttpContext = helper.ViewContext.HttpContext;
            var httpContextFactory = GetServiceOrFail<IHttpContextFactory>(currentHttpContext);
            var actionInvokerFactory = GetServiceOrFail<IActionInvokerFactory>(currentHttpContext);
            var actionSelector = GetServiceOrFail<IActionDescriptorCollectionProvider>(currentHttpContext);

            // creating new action invocation context
            var routeData = new RouteData();
            var routeParams = new RouteValueDictionary(parameters ?? new { });
            var routeValues = new RouteValueDictionary(new { area, controller, action });
            var newHttpContext = httpContextFactory.Create(currentHttpContext.Features);

            newHttpContext.Response.Body = new MemoryStream();

            foreach (var router in helper.ViewContext.RouteData.Routers)
                routeData.PushState(router, null, null);

            routeData.PushState(null, routeValues, null);
            routeData.PushState(null, routeParams, null);

            var actionDescriptor = actionSelector.ActionDescriptors.Items.First(i => i.RouteValues["Controller"] == controller && i.RouteValues["Action"] == action);
            var actionContext = new ActionContext(newHttpContext, routeData, actionDescriptor);

            // invoke action and retreive the response body
            var invoker = actionInvokerFactory.CreateInvoker(actionContext);
            string content = null;

            await invoker.InvokeAsync().ContinueWith(task =>
            {
                if (task.IsFaulted)
                {
                    content = task.Exception.Message;
                }
                else if (task.IsCompleted)
                {
                    newHttpContext.Response.Body.Position = 0;
                    using (var reader = new StreamReader(newHttpContext.Response.Body))
                        content = reader.ReadToEnd();
                }
            });

            return new HtmlString(content);
        }

        private static TService GetServiceOrFail<TService>(HttpContext httpContext)
        {
            if (httpContext == null)
                throw new ArgumentNullException(nameof(httpContext));

            var service = httpContext.RequestServices.GetService(typeof(TService));

            if (service == null)
                throw new InvalidOperationException($"Could not locate service: {nameof(TService)}");

            return (TService)service;
        }
    }
}

The following examples were successfully tested:

@Html.RenderAction("About")
@Html.RenderAction("About", "Home")
@Html.RenderAction("About", new { data1 = "test1", data2 = "test2" })
@Html.RenderAction("About", "Home", new { data1 = "test1", data2 = "test2" })
M.R.T
  • 746
  • 8
  • 20
2

Workaround by Aries for the Helper Extension is no more workable for Net Core 2.0 as IActionSelectorDecisionTreeProvider has been removed from the newer version. See the link below.

https://github.com/Microsoft/aspnet-api-versioning/issues/154

aquariens
  • 71
  • 4
1

For the asp.net core 2 solution provided by Yepeekai, please add the following in your Startup.cs:

services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();

This is no longer registered by default in 2.0.

Jordan B
  • 109
  • 8
1

My solution is based on the overhauled code from Yepeekai based on the solution of Aries.

Since actionSelector.SelectCandidates did not return all routes, i had to change to IActionDescriptorCollectionProvider. This resulted in getting multiple routes for the desired action, which made actionSelector.SelectBestCandidate fail. So i did the controller and action filtering on the possible routes manually.

Remember, there are more values you might want to consider for route filtering as for example "area".


The full code:

In your StartUp.cs: (Remember to use your own implementation of HttpContextAccessor, without the namespace, the framework version will be used!)

builder.Services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
builder.Services.AddSingleton<IHttpContextAccessor, Helper.HttpContextAccessor>();

Custom HttpContextAccessor from Yepeekai:

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

Extension for html helper:

public static class HtmlHelperViewExtensions
{
    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 result = RenderActionAsync(helper, action, controller, area, parameters).GetAwaiter().GetResult();

        return 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 = serviceProvider.GetRequiredService<IActionContextAccessor>();
        var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
        var actionSelector = serviceProvider.GetRequiredService<IActionSelector>();
        var actionCollectionProvider = serviceProvider.GetRequiredService<IActionDescriptorCollectionProvider>();

        // 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 originalActionContext = actionContextAccessor.ActionContext;
        var originalhttpContext = httpContextAccessor.HttpContext;
        try
        {
            IReadOnlyList<ActionDescriptor> candidates = actionCollectionProvider.ActionDescriptors.Items.Select(x => x as ControllerActionDescriptor)
                .Where(x =>
                string.Equals(x.ControllerName, controller, StringComparison.CurrentCultureIgnoreCase) &&
                string.Equals(x.ActionName, action, StringComparison.CurrentCultureIgnoreCase)).ToList();

            var actionDescriptor = actionSelector.SelectBestCandidate(routeContext, candidates);

            var newHttpContext = serviceProvider.GetRequiredService<IHttpContextFactory>().Create(helper.ViewContext.HttpContext.Features);
            if (newHttpContext.Items.ContainsKey(typeof(IUrlHelper)))
            {
                newHttpContext.Items.Remove(typeof(IUrlHelper));
            }

            using (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().ConfigureAwait(false);

                newHttpContext.Response.Body.Position = 0;
                using (var reader = new StreamReader(newHttpContext.Response.Body))
                {
                    return new HtmlString(reader.ReadToEnd());
                }
            };
        }
        catch (Exception ex)
        {
            logger.Error($"Failed to invoke action {action} on controller {controller}.", 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));
            }
        }
    }
}

Important node

I ran into strange problems with the MemoryStream being disposed when accessing. Source of the problem seemed to be multiple partials used in the layout and the actual index html. Make sure the action you are invoking is not returning this.View() but this.PartialView() or you will run into the same problem or worse.

Please let me know if anything needs to be improved.

Robert
  • 11
  • 3
0

M.R.T2017 said : ... The following examples were successfully tested: ...

First thanks for your sharing.

But this overload method may be cause "HTTP ERROR 500" :

@Html.RenderAction("About")

Because 'controller' maybe lower case controller name, ex. "home", "grid", etc. :

helper.ViewContext.RouteData.Values["controller"]

You need capitalize the controller name, ex. "grid" -> "Grid", because the controller class name is case-sensitive in Assembly, action name is the same.

*Visual Studio 2019/NET Core 2.2.

Chris Chi
  • 1
  • 1
  • How to return the HtmlString directly without View ? Anyone knows ? Because without view, the Html.Render/RenderAction will throw HTTP ERROR 500. – Chris Chi Nov 04 '19 at 13:36
  • Sorry, HTTP ERROR 500 was caused by razor code (@ { Layout = ...; ... }) not Html.Render/RenderAction and those method works fine. But Html.Render/RenderAction will always return empty string while the action return the HtmlString (without corresponding view), Anyone knows how to solve the problem ? – Chris Chi Nov 05 '19 at 08:00
0

I tried it on Umbraco V11 (.NET 7.0) with a complex Controller Constructor and it works well:

XXController(ILogger<XXController> logger, ICompositeViewEngine compositeViewEngine, IUmbracoContextAccessor umbracoContextAccessor,
                IPublishedValueFallback publishedValueFallback, IExamineManager ExamineManager, IPublishedContentQuery contentquery, IContentService contentService,
                IWebHostEnvironment? webHost, IMemoryCache memoryCache, IMemberService memberService, IUserService userService, IHttpContextAccessor contextAccessor)
            : base(logger, compositeViewEngine, umbracoContextAccessor)
Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77
  • Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. **Would you kindly [edit] your answer to include additional details for the benefit of the community?** – Jeremy Caney Aug 19 '23 at 01:06