You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core分页实现抛出InvalidOperationException错误排查

问题与解决方案

错误场景

加载https://localhost:7242/product页面时触发以下错误,页面需实现分页功能:

InvalidOperationException: 传入ViewDataDictionary的模型项类型为'System.Linq.Enumerable+d__2313[<>f__AnonymousType02[WebApplication3105.Models.Product,System.Collections.Generic.IEnumerable1[WebApplication3105.Models.Category]],WebApplication3105.Models.Category,WebApplication3105.Models.ProductCategory]',但此ViewDataDictionary实例需要类型为'X.PagedList.IPagedList1[WebApplication3105.Models.ProductCategory]'的模型项。

相关代码

模型类

namespace WebApplication3105.Models
{
    public class Product
    {
        public int ProductId { get; set; }
        public string ProductName { get; set; }
        public int CategoryId { get; set; }
        public Category Category { get; set; }
    }
}

namespace WebApplication3105.Models
{
    public class Category
    {
        public int CategoryId { get; set; }
        public string CategoryName { get; set; }
        public ICollection<Product> Product { get; set; }
    }
}

namespace WebApplication3105.Models
{
    public class ProductCategory
    {
        public Product product { get; set; } 
        public Category category { get; set; }  
    }
}

控制器代码

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; 
using PagedList.Mvc;
using WebApplication3105.Data;
using WebApplication3105.Models;
using X.PagedList;

namespace WebApplication3105.Controllers
{
    public class ProductController : Controller
    {
        private readonly MVCDbContext dbContext;

        public ProductController(MVCDbContext dbContext)
        {
            this.dbContext = dbContext;
        }

        [HttpGet]
        public async Task<IActionResult> Index(int? page)
        {
            List<Product> products = dbContext.Product.ToList();
            List<Category> categories = dbContext.Category.ToList();
            int recordsPerPage = 2;

            var data = from p in products
                       join c in categories on p.CategoryId equals c.CategoryId into table1
                       from c in table1.ToList().ToPagedList(page ?? 1, recordsPerPage)
                       select new ProductCategory
                       {
                           product = p,
                           category = c
                       };
            
            return View(data);
        }

        [HttpGet]
        public IActionResult AddProduct()
        {
            return View();
        }

        [HttpPost]
        public async Task<IActionResult> AddProduct(Product productRequest)
        {
            var product = new Product()
            {
                ProductName = productRequest.ProductName,
                CategoryId = productRequest.CategoryId
            };

            await dbContext.Product.AddAsync(product);
            await dbContext.SaveChangesAsync();
            return RedirectToAction("Index");
        }
    }
}

视图页面代码

@*
    For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
*
@{}

@using X.PagedList.Mvc.Core;
@using X.PagedList
@model IPagedList<WebApplication3105.Models.ProductCategory>

<h1 class="text-center text-uppercase">
    List of products
</h1>

<table class="table table-striped table-bordered">
    <thead class="bg-dark text-white">
        <tr>
            <th>Product Id</th>
            <th>Product Name</th>
            <th>Category Id</th>
            <th>Category Name</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var data in Model)
        {
            <tr>
                <th>@Html.DisplayFor(modelItem =>data.product.ProductId)</th>
                <th>@Html.DisplayFor(modelItem =>data.product.ProductName)</th>
                <th>@Html.DisplayFor(modelItem =>data.product.CategoryId)</th>
                <th>@Html.DisplayFor(modelItem =>data.category.CategoryName)</th>     
            </tr>
        }
    </tbody>
</table>
<div>
   <div class="pagedList">
        @Html.PagedListPager(Model, page => Url.Action("Index", new { page }))
    </div>
</div>

错误原因

控制器中分页逻辑错误:在join后的table1上调用ToPagedList,导致最终返回的data是Linq的SelectManyIterator类型,而非视图所需的IPagedList<ProductCategory>。同时,提前调用ToList()会把所有数据加载到内存,性能低下。

修复方案

修改控制器的Index方法,先正确关联Product和Category,再对整体结果应用分页:

[HttpGet]
public async Task<IActionResult> Index(int? page)
{
    int recordsPerPage = 2;
    int pageNumber = page ?? 1;

    // 直接通过EF关联查询,避免提前加载所有数据到内存
    var productCategories = await dbContext.Product
        .Join(dbContext.Category, 
              p => p.CategoryId, 
              c => c.CategoryId, 
              (p, c) => new ProductCategory
              {
                  product = p,
                  category = c
              })
        .ToPagedListAsync(pageNumber, recordsPerPage);

    return View(productCategories);
}

额外优化

也可以利用EF的导航属性简化查询,代码更简洁:

[HttpGet]
public async Task<IActionResult> Index(int? page)
{
    int recordsPerPage = 2;
    int pageNumber = page ?? 1;

    var productCategories = await dbContext.Product
        .Include(p => p.Category) // 加载关联的Category
        .Select(p => new ProductCategory
        {
            product = p,
            category = p.Category
        })
        .ToPagedListAsync(pageNumber, recordsPerPage);

    return View(productCategories);
}

修改后,返回给视图的就是IPagedList<ProductCategory>类型的模型,与视图要求一致,同时分页逻辑正确,还能利用EF的查询优化减少内存占用。

内容的提问来源于stack exchange,提问作者ashish yadav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 00:45:02