InvalidOperationException for dropdown filter in ASP.NET Core MVC

117 Views Asked by At

I have created a global dropdown filter on my navbar. I get the error:

An unhandled exception occurred while processing the request.

InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'System.String', but this ViewDataDictionary instance requires a model item of type 'Website.ViewModels.CardsViewModel.CardsViewModel'.

Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary.EnsureCompatible(object value)

Here is my code -

FilterController.cs:

  • The _cache.Set() in FilterController.cs redirects the code to PostController.cs when I open the Post page in website. It is only then that my code gives error.
public IActionResult Index(string postCategory)
{
    var categoryLst = new List<string>();

    var categoryQry = from p in _context.Posts
                      orderby p.PostCategory 
                      select p.PostCategory;

    categoryLst.AddRange(categoryQry.Distinct());
    ViewBag.movieGenre = new SelectList(categoryLst);

    var posts = from c in _context.Posts
                select c;

    if (!string.IsNullOrEmpty(postCategory))
    {
        posts = posts.Where(x => x.PostCategory == postCategory);
    }

    _cache.Set("category", postCategory);
    ViewBag.PostCategory = postCategory;

    string url = Request.Headers["Referer"].ToString();

    return Redirect(url);
}

PostController.cs:

  • The _cache.Set() in FilterController.cs redirects the code here.
  • I have edited the code below.
[HttpGet]
public async Task<IActionResult> Index()
{
    var CardPostVM = new CardsViewModel
    {
        PostCard = await _context.Posts.ToListAsync()
    };

    var cached = _cache.TryGetValue("category", out var postCategory);

    if (cached)
    {
        return View(postCategory);
    }

    return View(CardPostVM);
}

(Reference files):

Using _PartialLayout _TopNavbar.cshtml for my navbar. Here is the view for the dropdown filter:

<!-- dropdown filter -->
<form class="ps-lg-5" asp-controller="Filter" asp-action="Index" id="category" method="get">
    <div class="input-group ps-lg-5">
        <select class="form-control shadow-none" name="postCategory" id="post-filter">
            <option>All</option>">
            @foreach (var item in cardsVM.PostCategories)
            {
                <!option value="@item.CategoryName"  @(item.CategoryName.ToString() == ViewBag.PostCategory?.ToString() ? "selected" : "")>@item.CategoryName</!option>
            }
        </select>
        <button class="btn btn-outline-success shadow-none me-2" type="submit"><i class='bx bx-filter'></i></button>
    </div>
</form>

CardsViewModel.cs (mentioned in the error):

public class CardsViewModel
{
    public List<PostModel>? PostCard { get; set; }
}

Update (Reference files):

_Layout.cshtml is where _TopNavbar.cshtml is mentioned.

<!DOCTYPE html>
<html lang="en">
<head>
    <!--Links-->
</head>

<body id="body-pd">

    <!--Top Nav bar-->
    <div>
        <partial name="~/Views/Shared/TopNavbar/_TopNavbar.cshtml" />
    </div>

    <!--Render Body-->
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <!--Links-->

    @await RenderSectionAsync("Scripts", required: false)

</body>
</html>

This is a _PartialLayout file _PostCards.cshtml that includes @model CardsViewModel:

@using RecipeWebsite.ViewModels.CardsViewModel

@model CardsViewModel

@foreach (var item in Model.PostCard)
{
    // Detail of Card where my data is displayed
    <div class="col mb-4">
        <div class="card shadow-sm">
            <img class="bd-placeholder-img card-img-top" src="@item.Image" width="157" height="236" alt="Card image cap">

            <!--Card Details-->
            <div class="card-body">
                <a asp-controller="Post" asp-action="Detail" asp-route-id="@item.Id" class="stretched-link"></a>
                <h6 class="card-text"> @item.Title </h6>
                <div class="d-flex justify-content-between align-items-center mb-2">
                    <p class="small text-secondary">@item.PostCategory</p>
                </div>            
            </div>        
        </div>
    </div> 
}

File where PostCards.cshtml is mentioned

@using RecipeWebsite.ViewModels.CardsViewModel

@model CardsViewModel

<main>

    <!-- Cards -->
    @if (Model.PostCard.Count != 0)
    {
        <div class="album py-5 bg-light">
            <div class="container">

                <div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-3">

                    <partial name="~/Views/Shared/Cards/_PostCards.cshtml" />

                </div>

            </div>
        </div>
    }
    else 
    {
        // Error
    }

</main>

Thank you

1

There are 1 best solutions below

0
ZNAXNOR On BEST ANSWER

Here is my answer

  • Change required in FilterController.cs and PostController.cs

FilterController.cs:

public async Task<IActionResult> Index(string postCategory)
{
    var filteredPost = from p in _context.Posts select p;

    if (!string.IsNullOrEmpty(postCategory))
    {
        ViewBag.PostCategory = postCategory;

        filteredPost = filteredPost.Where(p => p.PostCategory == postCategory);
    }

    var filteredCategory = new CardsViewModel
    {
        PostCard = await filteredPost.ToListAsync()
    };

    _cache.Set("filteredCategory", filteredCategory);

    string url = Request.Headers["Referer"].ToString();

    return Redirect(url);
}

PostController.cs

[HttpGet]
public async Task<IActionResult> Index()
{
    var CardPostVM = new CardsViewModel
    {
        PostCard = await _context.Posts.ToListAsync()
    };

    var cached = _cache.TryGetValue("filteredCategory", out var filteredCategory);
    if (cached)
    {
        return View(filteredCategory);
    }

    return View(CardPostVM);
}