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

EF Core 6.0 Web API中一对多关联GET请求如何加载嵌套数据?

问题描述

我用EF Power Tools生成了以下实体类和DbContext,底层表通过CategoryId建立一对多关联:

Category.cs

public partial class Category
{
    public Category()
    {
        Folders = new HashSet<Folder>();
        Reports = new HashSet<Report>();
    }

    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
    public string CategoryDescription { get; set; }
    public string ImagePath { get; set; }

    public virtual ICollection<Folder> Folders { get; set; }
    public virtual ICollection<Report> Reports { get; set; }
}

Folder.cs

public partial class Folder
{
    public Folder()
    {
        Reports = new HashSet<Report>();
    }

    public int FolderId { get; set; }
    public string FolderName { get; set; }
    public int CategoryId { get; set; }
    public string FolderDescription { get; set; }
    public string FolderImagePath { get; set; }

    public virtual Category Category { get; set; }
    public virtual ICollection<Report> Reports { get; set; }
}

DbContext配置

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Category>(entity =>
    {              
        entity.HasKey(e => e.CategoryId)
            .IsClustered(false);

        entity.ToTable("Category");

        entity.Property(e => e.CategoryId)
            .ValueGeneratedNever()
            .HasColumnName("CategoryID");

        entity.Property(e => e.CategoryDescription)
            .HasMaxLength(300)
            .IsUnicode(false);

        entity.Property(e => e.CategoryName)
            .HasMaxLength(100)
            .IsUnicode(false);

        entity.Property(e => e.ImagePath)
            .HasMaxLength(250)
            .IsUnicode(false);              
    } );

    modelBuilder.Entity<Folder>(entity =>
    {
        entity.HasKey(e => e.FolderId)
            .IsClustered(false);

        entity.ToTable("Folder");

        entity.Property(e => e.FolderId)
            .ValueGeneratedNever()
            .HasColumnName("FolderID");

        entity.Property(e => e.CategoryId).HasColumnName("CategoryID");

        entity.Property(e => e.FolderDescription)
            .HasMaxLength(300)
            .IsUnicode(false);

        entity.Property(e => e.FolderImagePath)
            .HasMaxLength(250)
            .IsUnicode(false);

        entity.Property(e => e.FolderName)
            .HasMaxLength(100)
            .IsUnicode(false);

        entity.HasOne(d => d.Category)
            .WithMany(p => p.Folders)
            .HasForeignKey(d => d.CategoryId)
            .OnDelete(DeleteBehavior.ClientSetNull)
            .HasConstraintName("FK_Folder_01");
    });

    OnModelCreatingPartial(modelBuilder);
}

我写了如下API接口:

[HttpGet]
public async Task<ActionResult<IEnumerable<Category>>> GetCategories()
{
    return await _context.Categories.ToListAsync();
}

返回结果里folders数组是空的:

[
{
"categoryId": 1,
"categoryName": "Some Name",
"folders": [],
"reports": []
},
{
"categoryId": 2,
"categoryName": "Another Name",
"folders": [],
"reports": []
},
]

尝试了以下代码也没解决:

public async Task<ActionResult<IEnumerable<Category>>> GetCategories()
{
    var query = (from r in _context.Categories
                 from bu in _context.Folders
                 where r.CategoryId == bu.CategoryId
                 select r
          ).ToListAsync();

    return await query;
}

请问如何让返回结果中的folders数组填充对应的关联数据?


解决方案

1. 使用Include显式加载关联数据

Entity Framework Core默认不会自动加载关联的导航属性(除非启用延迟加载),要获取关联的Folders,需要用Include方法指定要加载的导航属性:

基础写法

[HttpGet]
public async Task<ActionResult<IEnumerable<Category>>> GetCategories()
{
    // 加载每个Category对应的Folders
    var categories = await _context.Categories
        .Include(c => c.Folders) // 显式加载Folders导航属性
        .ToListAsync();
    
    return categories;
}

如果需要加载多层关联(比如Folders下的Reports)

如果还需要返回每个Folder对应的Reports,可以用ThenInclude:

var categories = await _context.Categories
    .Include(c => c.Folders)
        .ThenInclude(f => f.Reports) // 继续加载Folders下的Reports
    .ToListAsync();

2. 为什么你之前的查询方式无效

你尝试的from r in _context.Categories from bu in _context.Folders这种写法是做了笛卡尔积查询,会返回重复的Category对象(每个Category对应多少Folder就返回多少次),而且并没有加载导航属性,只是筛选出有对应Folder的Category,但EF不会自动填充Folders集合,所以返回的结果里folders还是空的。

3. 关于延迟加载的说明

虽然可以启用延迟加载来自动加载关联数据,但不推荐在Web API场景使用:

  • 需要确保导航属性是virtual(你的实体已经满足),并且在DbContext中启用延迟加载:
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("你的连接字符串")
            .UseLazyLoadingProxies(); // 启用延迟加载代理
    }
    
  • 但延迟加载会在序列化实体时触发额外的数据库查询,可能导致N+1问题,而且容易引发循环引用(Category包含Folders,Folder又包含Category),需要额外配置序列化忽略循环引用,所以显式加载(Include)是更可控的方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 22:40:29