ASP.NET Core 5.0 MVC : CS1061 error using Html.RenderAction

Viewed 1703

I am working on a project using ASP.NET Core 5.0 to build a web app (MVC). I tried to use the Html.RenderAction extension method but I get the following error.

enter image description here

COMPILE ERROR:

Html.RenderAction  CS1061: IHtmlHelper<dynamic> does not contain a definition for 'RenderAction'

What I'm I not doing right?

To give more context, I want to render a partial view in _Layout.cshtml using data loaded directly from the database.

This is the content of my _Layout.cshtml

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384- EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<link rel="stylesheet" href="~/dist/css/main.css" media="all" />
<title>@ViewData["Title"]: my webpage</title>
</head>
<body id="body">
<!-- Header -->
<header id="header">
    <!-- Navigation -->
    <nav id="nav">
        <partial name="../Shared/Components/_navBar.cshtml" />
        <partial name="../Shared/Components/_headerSearchBar.cshtml" />
    </nav>

    @{ Html.RenderAction("GetCategories"); }

</header>

<!-- Main -->
<main role="main" class="main-wrapper">
    <div class="home main-content">
        @RenderBody()
    </div>
</main>

<!-- Footer -->
<footer class="footer">
    <partial name="../Shared/Components/_footer.cshtml" />
</footer>
<script src="~/js/script.js" asp-append-version="true"></script>
@*<script src="~/js/scriptanim.js" asp-append-version="true"></script>*@
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

This is the action method in the controller that loads data to the partial view

        public async Task<IActionResult> GetCategories()
        {
            var categories = await _fashionBuyRepository.GetAllCategoriesAsync();

            return PartialView("_productCategoriesHeader", categories);
        }
3 Answers

In .NET 5, @Html.RenderAction no longer works as to my knowledge.

I believe you can use

@await Html.PartialAsync("GetCategories")

or

@await Html.RenderPartialAsync("GetCategories")

instead of it. There may be other options, check the .NET documentation.

I took the ViewComponent method as suggested by @Michael to solve this problem and was the most optimal for me.

I created a CategoriesViewComponent in a Components folder as follows:

using eFashionBuy.Repository;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace eFashionBuy.Components
{
    public class CategoriesViewComponent : ViewComponent
    {
        private readonly IFashionBuyRepository _fashionBuyRepository;

        public CategoriesViewComponent(IFashionBuyRepository fashionBuyRepository)
        {
            _fashionBuyRepository = fashionBuyRepository;
        }

        public async Task<IViewComponentResult> InvokeAsync()
        {
            var categories = await _fashionBuyRepository.GetAllCategoriesAsync();

            return View(categories);
        }
   }
}

The view associated with this component is called Default.cshtml (a partial view) in the location /Shared/Components/Categories/Default.cshtml as follows:

@model IEnumerable<Category>;

@* Product Categories mobile menu *@
@foreach (var category in Model)
{
    <li class="categories__item">
        <a class="categories-link"
           asp-controller="Catgeories"
           asp-action="Category"
           asp-route-id="@category.CategoryID"
           asp-route-category="@category.CategoryName">

           <span>@category.CategoryName</span>
        </a>
    </li>
}

The component is now ready for use. I simply called it as follows where I want to use it

<ul class="categories__items">  
    <!-- This is how I call the component using TagHelper -->  
    <vc:categories />
</ul>

This helped me avoid most of the nuances with RenderAction which was my initial approach. I will use this knowledge to simplify future designs.

There are several ways to do this. One of them to use component. But it will need some javascript code to use it

Another, more simple for a novice way, you have to create a base view model that you will have to use for ALL your views that are using this layout

using Microsoft.AspNetCore.Mvc.Rendering;

public interface IBaseViewModel
{
     int CategoryId { get; set; }
     List<Category> Categories{ get; set; }
}

public class BaseViewModel : IBaseViewModel
{
    public int CategoryId { get; set; }
    public List<Category> Categories { get; set; }
}

action

public async Task<IActionResult> Index()
{
 var baseViewModel=new BaseViewModel();
 baseViewModel.Categories=  await _fashionBuyRepository.GetAllCategoriesAsync();
 return View(baseViewModel);
}

use this model inside of the partial view

@model BaseViewModel

layout

@model IBaseViewModel // you can omit it but I like to have it explicitly

@if(Model!=null && Model.Categories!=null && Model.Categories.Count > 0)
{
 <partial name="_productCategoriesHeader" />
}

for another view you can create this action code

public IActionResult MyAction()
var myActionViewModel= new MyActionViewModel {
..... your init code
}

 InitBaseViewModel(myActionViewModel);

return View(myActionViewModel)
}

public class MyActionViewModel : BaseViewModel
//or 
public class MyActionViewModel : IBaseViewModel
{
    public .... {get; set;}
}
Related