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

ASP.NET MVC中基于LINQ构建多层嵌套ViewModel的模型绑定问题求助

Hey there! Let's work through this together. The core issue with your current code is that you're trying to map flat query results directly to your nested ViewModel structure without grouping related data first—plus there are some syntax mismatches between your property types and assignments.

First, Let's Fix the ViewModel Mapping Logic

Your LINQ query returns flattened data (one row per item-template-category combination), so we need to group this data first to match the nested structure of TemplateViewModel. Here's the corrected code:

// Start with your existing query (we'll reuse it for grouping)
var query = _sqlDbContext.RequestTemplates 
    .Join(_sqlDbContext.RequestTemplateItems, w => w.RequestTemplateID, m => m.RequestTemplateID, (w, m) => new { w.TemplateName, m.RequestCategoryID, m.ItemName, m.DueDate, m.Notes }) 
    .Join(_sqlDbContext.RequestCategories, k => k.RequestCategoryID, opt => opt.RequestCategoryID, (k, opt) => new { k.ItemName, k.Notes, k.TemplateName, opt.RequestCategoryName, opt.RequestCategoryID, k.DueDate });

// Convert the flat query results to your nested ViewModel structure
var templateViewModels = query
    // First group all records by TemplateName to create each top-level TemplateViewModel
    .GroupBy(q => q.TemplateName)
    .Select(templateGroup => new TemplateViewModel
    {
        templateName = templateGroup.Key,
        requests = templateGroup
            // Next, group each template's records by category to create RequestViewModels
            .GroupBy(item => new { item.RequestCategoryID, item.RequestCategoryName })
            .Select(categoryGroup => new RequestViewModel
            {
                // Fix: Your RequestViewModel.category is a List<CategoryViewModel>
                category = new List<CategoryViewModel>
                {
                    new CategoryViewModel
                    {
                        categoryId = categoryGroup.Key.RequestCategoryID,
                        categoryName = categoryGroup.Key.RequestCategoryName
                    }
                },
                // Map all items in this category to ItemViewModel and convert to List
                items = categoryGroup
                    .Select(item => new ItemViewModel
                    {
                        itemName = item.ItemName,
                        dueDate = item.DueDate,
                        notes = item.Notes
                    })
                    .ToList()
            })
            .ToList()
    })
    .ToList();

Why Your Original Code Wasn't Working

Let's break down the key issues:

  1. Incorrect property assignment: model.templateName = query.Select(c => c.TemplateName); tries to assign a collection of strings to a single string property—grouping by TemplateName fixes this by using the group key as the single template name.
  2. Missing grouping: You were creating a new RequestViewModel for every row, instead of grouping items under their respective categories within each template.
  3. Syntax/type mismatches:
    • category is a List<CategoryViewModel>, but you tried to assign a single instance directly.
    • Your items initialization had invalid syntax (you need to instantiate ItemViewModel objects inside the list).

A Quick Suggestion for Better Semantics

Looking at your ViewModel structure, it seems odd that RequestViewModel.category is a list—logically, a single request should belong to one category, not multiple. If that's the case, update your RequestViewModel to use a single CategoryViewModel instead:

public class RequestViewModel { 
    public CategoryViewModel category { get; set; } // Changed from List<> to single instance
    public List<ItemViewModel> items { get; set; } 
}

Then adjust the mapping code to remove the list wrapper for category:

category = new CategoryViewModel
{
    categoryId = categoryGroup.Key.RequestCategoryID,
    categoryName = categoryGroup.Key.RequestCategoryName
},

Bonus: Follow C# Naming Conventions

As a best practice, use PascalCase for class properties (e.g., TemplateName instead of templateName) to align with .NET standards. This will make your code more readable and avoid potential issues with model binding later.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 09:17:30