解决ViewModel InvalidOperationException模型类型不匹配报错
我正在开发一个产品竞赛提交网站,购买产品的用户可输入序列号参与竞赛。用户提交的数据及序列号会被标记为“已使用序列号”,要求页面仅显示用户一次并展示其提交次数,且每个用户每个产品最多可参与两次。
我创建了OverviewModel和OverviewContestModel两个模型存储表格数据,并创建包含二者的OverviewContestViewModel。在视图顶部声明@model UmbracoAcmeMia.ViewModels.OverviewContestViewModel后,却出现模型类型不匹配错误。作为C#和.NET新手,我尝试修改@model路径、设置断点均未解决问题。
视图代码
@model UmbracoAcmeMia.ViewModels.OverviewContestViewModel @{ ViewData["Title"] = "Index"; Layout = "~/Views/Shared/_Layout.cshtml"; } <!-- 剩余视图代码略 -->
控制器代码
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); }
模型及ViewModel代码
// OverviewModel public class OverviewModel { public DateTime UsedDate { get; set; } /* 其他属性略 */ } // OverviewContestModel public class OverviewContestModel { public string FirsteName { get; set; } /* 其他属性略 */ } // OverviewContestViewModel public class OverviewContestViewModel { public OverviewModel OverviewModel { get; set; } public OverviewContestModel OverviewContestModel { get; set; } }
错误信息
InvalidOperationException: 传入ViewDataDictionary的模型项类型为'Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[UmbracoAcmeMia.Models.OverviewContestModel]',但该ViewDataDictionary实例要求的模型项类型为'UmbracoAcmeMia.ViewModels.OverviewContestViewModel'。
核心问题
控制器返回的是IQueryable<OverviewContestModel>类型的用户集合,但视图期望接收的是单个OverviewContestViewModel对象,两者类型完全不匹配,直接导致报错。此外代码还存在两个额外问题:
- 变量名
OverviewContestViewModel与类名重名,不符合C#小驼峰命名规范 - 用
Distinct()无法正确按用户去重,因为UsedSerialNumbers是序列号记录,每条对应一个用户,直接去重不会按用户分组统计
修复步骤
1. 调整ViewModel结构
因为页面要展示多个用户的列表,ViewModel需要包含OverviewContestModel的集合,修改后的ViewModel:
public class OverviewContestViewModel { // 若需要OverviewModel的数据可保留,否则可移除 public OverviewModel OverviewModel { get; set; } // 存储用户列表及提交次数 public List<OverviewContestModel> UserContestList { get; set; } }
2. 修正控制器代码
按用户ID分组统计提交次数,再封装到ViewModel中返回:
public IActionResult Index() { // 按用户分组,统计每个用户的提交次数 var userContestData = _context.UsedSerialNumbers .Include(x => x.Users) .GroupBy(x => x.Users.Id) // 按用户ID分组确保去重 .Select(g => new OverviewContestModel() { FirstName = g.First().Users.FirstName, // 修正拼写错误FirsteName→FirstName LastName = g.First().Users.LastName, Email = g.First().Users.Email, Birthday = g.First().Users.Birthday, UsedCount = g.Count() // 直接统计分组内的记录数即提交次数 }) .ToList(); // 实例化ViewModel并赋值 var viewModel = new OverviewContestViewModel() { UserContestList = userContestData // 若需赋值OverviewModel,可在此添加查询逻辑 // OverviewModel = _context.Overviews.FirstOrDefault() }; return View(viewModel); }
3. 更新视图代码
修改模型声明并遍历用户列表展示数据:
@model UmbracoAcmeMia.ViewModels.OverviewContestViewModel @{ ViewData["Title"] = "Index"; Layout = "~/Views/Shared/_Layout.cshtml"; } <!-- 展示用户列表及提交次数 --> <table class="table"> <thead> <tr> <th>姓名</th> <th>邮箱</th> <th>生日</th> <th>提交次数</th> </tr> </thead> <tbody> @foreach(var user in Model.UserContestList) { <tr> <td>@user.FirstName @user.LastName</td> <td>@user.Email</td> <td>@user.Birthday.ToString("yyyy-MM-dd")</td> <td>@user.UsedCount</td> </tr> } </tbody> </table>
额外优化建议
- 修复
OverviewContestModel中的拼写错误:FirsteName改为FirstName - 确保
UsedSerialNumbers与Users的EF关联关系配置正确,避免Include失效 - 在提交序列号时添加验证逻辑,查询当前用户对应产品的提交次数是否小于2,实现“每个用户每个产品最多参与两次”的要求
内容的提问来源于stack exchange,提问作者Maria Gath

