Maria Gath
Maria Gath

Reputation: 15

How to make my viewModel work (InvalidOperationException - The model item passed into the ViewDataDictionary is of type)

Submit form Tabel with all submissions

I am creating a website where everyone who has bought a product can participate in a competition by entering their serial number from the product.

They enter their data and the serial number which is saved is "used serialnumber" the user must only be shown once and at the same time the number of times they have submitted must be visible (Count).

Each user can participate in the competition twice for each product.

I have created two models that together contain the data that must be in the table and therefore I have created a view model that contains both models.

By entering @model OverviewContestViewModel at the top of my view, I must then be able to access data from both OverviewModel and OverviewContestModel.

I'm trying to use my view model which contains two models, but I'm doing something wrong.

When I insert my data into HTML I get the following error.

PS: I am quite new to C# and .NET.

Here is my controller, index and other models:

@model UmbracoAcmeMia.ViewModels.OverviewContestViewModel

Index:

@{
    ViewData["Title"] = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
    
}

@*@if(Model.Count() > 0) {*@

}

<div class="container tableWrapper">
    <h1>SUBMISSION OVERVIEW</h1>
    <h3>135 submissions</h3>

<div id="toolbar">
        <select class="form-control">
                <option value="">Export Basic</option>
                <option value="all">Export All</option>
                <option value="selected">Export Selected</option>
        </select>
</div>

<table id="table" 
            data-toggle="table"
            data-search="true"
            data-virtual-scroll="true"
            data-show-columns="true"
            data-height="600"
            data-search-align="left"
            data-filter-control="true" 
            data-show-export="true"
            data-click-to-select="true"
            data-pagination="true"
            data-pagination-v-align="bottom"
            data-pagination-h-align="left"
            data-pagination-detail-h-align="right"
            data-toolbar-align="right"
            data-toolbar="#toolbar">
            
    <thead>
        <tr>
            <th data-field="state" data-checkbox="true"></th>
            <th data-field="Submit Date" data-filter-control="input" data-sortable="true">Submit Date</th>
            <th data-field="Name" data-filter-control="select" data-sortable="true">Name</th>
            <th data-field="Birthday" data-filter-control="select" data-sortable="true">Birthday</th>
            <th data-field="Email" data-filter-control="select" data-sortable="true">Email</th>
            <th data-field="SerialNumber" data-filter-control="select" data-sortable="true">Serial Number</th>
            <th data-field="Submit Submit Datecount" data-sortable="true">submit count</th>
        </tr>
    </thead>
        <tbody class=" my-custom-scrollbar">
        @*@foreach (var item in Model)*@
        {
        <tr>
            <td class="bs-checkbox "><input data-index="0" name="btSelectItem" type="checkbox"></td>
            <td>@Model.OverviewContestModel.FirsteName</td>
            <td>@Model.OverviewContestModel.FirsteName</td>
            <td>@Model.OverviewContestModel.Birthday</td>
            <td>@Model.OverviewContestModel.Email</td>
            <td>@Model.OverviewModel.Value</td>
            <td>@Model.OverviewContestModel.UsedCount</td>
        </tr>
        @*}for*@
    </tbody>
</table>
</div>

<script>

    var $table = $('#table');
    $(function () {
        $('#toolbar').find('select').change(function () {
            $table.bootstrapTable('refreshOptions', {
                exportDataType: $(this).val()
            });
        });
    })

    var trBoldBlue = $("table");

    $(trBoldBlue).on("click", "tr", function () {
        $(this).toggleClass("bold-blue");
    });
</script>

Controller:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UmbracoAcmeMia.DataModels;
using UmbracoAcmeMia.Models;
using UmbracoAcmeMia.ViewModels;

namespace UmbracoAcmeMia.Controllers
{
    public class OverviewController : Controller
    {
        private readonly Context _context;

        public OverviewController(Context context) => _context = context;

        public IActionResult Index()
        {
            var usedSerialNumber = _context.UsedSerialNumbers.ToList();
            var userList = _context.UsedSerialNumbers.Include(x => x.Users).Distinct();
            var OverviewContestViewModel = userList.Select(x => new OverviewContestModel()
            {
                FirsteName = x.Users.FirstName,
                LastName = x.Users.LastName,
                Email = x.Users.Email,
                Birthday = x.Users.Birthday,
                UsedCount = usedSerialNumber.Where(y => y.Value == x.Value).Count(),
            });

            return View(OverviewContestViewModel);
        }
    }
}

Models:

using System;
using System.ComponentModel.DataAnnotations;
using UmbracoAcmeMia.extensions;
using RequiredAttribute = System.ComponentModel.DataAnnotations.RequiredAttribute;
using System.ComponentModel;
using UmbracoAcmeMia.ViewModels;

namespace UmbracoAcmeMia.Models
{
    public class OverviewModel
    {
        public DateTime UsedDate  { get; set; }
        public int UserId { get; set; }
        public int SerialNumberId { get; set; }
        public int Value { get; set; }
    }

    public class OverviewContestModel
    {
        public string FirsteName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public DateTime Birthday { get; set; }
        public int UsedCount { get; set; }
    }
}

ViewModel:

using System.Collections.Generic;
using UmbracoAcmeMia.Models;

namespace UmbracoAcmeMia.ViewModels
{
    public class OverviewContestViewModel
    {
        public OverviewModel OverviewModel { get; set; }
        public OverviewContestModel OverviewContestModel { get; set; }
    }
}

Error:

An unhandled exception occurred while processing the request

InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[UmbracoAcmeMia.Models.OverviewContestModel]', but this ViewDataDictionary instance requires a model item of type 'UmbracoAcmeMia.ViewModels.OverviewContestViewModel'. Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary.EnsureCompatible(object value)

InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[UmbracoAcmeMia.Models.OverviewContestModel]', but this ViewDataDictionary instance requires a model item of type 'UmbracoAcmeMia.ViewModels.OverviewContestViewModel'

I have tried to change the path @model.... but I get the same error. I've also tried looking in my controller but I'm simply not skilled enough to spot what I'm doing wrong.

I've also tried setting breakpoints but I still can't see what the problem is.

Upvotes: 0

Views: 341

Answers (1)

Steve
Steve

Reputation: 216293

Look at this part of your code:

var OverviewContestViewModel = userList.Select(x => new OverviewContestModel()
{
    FirsteName = x.Users.FirstName,
    LastName = x.Users.LastName,
    Email = x.Users.Email,
    Birthday = x.Users.Birthday,
    UsedCount = usedSerialNumber.Where(y => y.Value == x.Value).Count(),

});
return View(OverviewContestViewModel);

You are returning a variable named exactly as the name of your class but this variable doesn't contain what you think. This variable, when materialized, will contain a List<OverviewContestModel> and of course this doesn't match the @model declaration in the View.

So you have two options.

Fix the model name in the view to be an IEnumerable<OverviewContestModel> and adjust the view code to take account for this change or build an actual instance of the OvervewContestViewModel required by the view with something like

var dataForOverview = userList.Select(x => new OverviewContestModel()
{
    FirsteName = x.Users.FirstName,
    LastName = x.Users.LastName,
    Email = x.Users.Email,
    Birthday = x.Users.Birthday,
    UsedCount = usedSerialNumber.Where(y => y.Value == x.Value).Count(),

});

var result = new OverviewContestViewModel();
result.OverviewContestModel = dataForOverview;
result.OverviewModel = ???? load the overviewmodel with some code ???
return View(result);

Note also that you should really try to avoid this naming confusion. Never name a variable or a property as the class name that it represents. I think it should be forbidden by default because the confusion that arises is very difficult to understand for newbies.

Upvotes: 1

Related Questions