【问题标题】:EF Core - Not loading all child entitiesEF Core - 不加载所有子实体
【发布时间】:2020-01-23 05:28:18
【问题描述】:

我有 3 个实体:

public partial class Category
{
    public int CategoryID { get; set; }
    public string? CategoryName { get; set; }

    public ICollection<BookCategory> BookCategory { get; set; }
}

public partial class BookCategory
{
    public int CategoryID { get; set; }
    public int BookID { get; set; }

    public Category Category { get; set; }
    public Book Book { get; set; }
}

public partial class Book
{
    public int BookID { get; set; }
    public string? Title { get; set; }
}

我希望返回一个类别数组,其中包含一个 BookCategory 的子数组,该数组与 Book 是一对一的。

使用这样的调用;

public async Task<List<Category>> test()
{
    var query = dbContext.Category
                .Include(p => p.BookCategory) 
                .ThenInclude(pc => pc.Book)
                .OrderBy(p => p.CategoryID) as IQueryable<Category>;

    var data = await query.ToListAsync();

    return data;
}

我有这样的虚拟数据:

insert into kiosk.Category (CategoryID, CategoryName) values (1, 'Horror')
insert into kiosk.Category (CategoryID, CategoryName) values (2, 'Fantasy')

insert into kiosk.Book (BookID, Title) values (1, 'Space shooty')
insert into kiosk.Book (BookID, Title) values (2, 'Elf shooty')

insert into kiosk.BookCategory (BookID, CategoryID) values (1, 2)
insert into kiosk.BookCategory (BookID, CategoryID) values (2, 2)

但是,我收到的对此请求的响应只给了我一个类别 2 的记录,而不是预期的两个。

[
    {
        "categoryID": 1,
        "categoryName": "Horror",
        "bookCategory": []
    },
    {
        "categoryID": 2,
        "categoryName": "Fantasy",
        "bookCategory": [
            {
                "categoryID": 2,
                "bookID": 1,
                "book": {
                    "bookID": 1,
                    "title": "Space shooty"
                }
            }
        ]
    }
]

数据库上下文:

    public class BooksDbContext : DbContext
    {

        public DbSet<Book> book { get; set; } 
        public DbSet<Category> Category { get; set; } 
        public DbSet<BookCategory> BookCategory { get; set; } 

        public BooksDbContext()
        { }

        public BooksDbContext(DbContextOptions options) : base(options)
        { }

        public BooksDbContext(string connectionString)
        {
            this.connectionString = connectionString;
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.HasDefaultSchema("dbo");

            modelBuilder.ApplyConfiguration(new BookCategoryConfiguration());
            modelBuilder.ApplyConfiguration(new CategoryTestConfiguration());
            modelBuilder.ApplyConfiguration(new BookConfiguration());

        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            if (!string.IsNullOrWhiteSpace(connectionString))
            {
                optionsBuilder.UseSqlServer(connectionString);
            }
        }
    }

三种配置如下

    public class BookCategoryConfiguration : IEntityTypeConfiguration<BookCategory>
    {
        public void Configure(EntityTypeBuilder<BookCategory> builder)
        {
            builder.ToTable("BookCategory");
            builder.HasKey(x => x.BookID);
            builder.HasKey(x => x.CategoryID);         

            builder.HasOne(a => a.Category).WithMany(b => b.BookCategory).HasForeignKey(c => c.CategoryID); // FK_POSCategoryProduct_POSCategory
        }
    }


    public class CategoryTestConfiguration : IEntityTypeConfiguration<Category>
    {
        public void Configure(EntityTypeBuilder<Category> builder)
        {
            builder.ToTable("Category");
            builder.HasKey(x => x.CategoryID);

            builder.Property(x => x.CategoryID).HasColumnName(@"CategoryID").HasColumnType("int").IsRequired();
            builder.Property(x => x.CategoryName).HasColumnName(@"CategoryName").HasColumnType("nvarchar").HasMaxLength(50);          

        }
    }


    public class BookConfiguration : IEntityTypeConfiguration<Book>
    {
        public void Configure(EntityTypeBuilder<Book> builder)
        {
            builder.ToTable("Book");
            builder.HasKey(x => x.BookID);

            builder.Property(x => x.BookID).HasColumnName(@"BookID").HasColumnType("int").IsRequired();
            builder.Property(x => x.Title).HasColumnName(@"Title").HasColumnType("nvarchar").HasMaxLength(50);
        }
    }

【问题讨论】:

  • 你确定数据库中有 2 个值吗?可以附上截图吗?有时我们在通过 UI 工具更改数据时忘记按“提交”按钮。还有一点:您应该忽略类 BookCategory 的属性类别。由于存在 I 循环引用。
  • 如果 BookCategory 对象,您能否尝试从数据库列表中进行选择。不止一个吗?
  • @TemaTre 他们提供了创建数据和模式的脚本,我认为问题应该只用那些重现 - 也不需要选择,他们提供了插入。还有为什么他们需要忽略该属性?
  • 数据库中有提供的脚本中显示的数据。
  • 存在循环引用。类别 -> BookCategory 和 BookCategory -> 类别。这可能是个问题。

标签: c# .net-core entity-framework-core


【解决方案1】:

模型通过显式连接实体表示标准的多对多关系。

问题是连接实体键的流畅映射:

builder.HasKey(x => x.BookID);
builder.HasKey(x => x.CategoryID);

HasKey 方法(即使不是所有流畅的 API 也是如此)不是附加的。后一个调用获胜(替换前一个)。

上述代码的效果是 EF Core 将CategoryID 视为BookCategory 表的唯一键,因此每个CategoryID 仅加载1条记录。

当然,这个想法是定义连接实体的标准复合主键。这是通过使用匿名类型(见 EF Core Keys 文档主题的复合键部分示例)实现的(类似于所有允许多个属性的 fluent API)。

将其应用于您的场景将用

替换上述两行
builder.HasKey(x => new { x.BookID, x.CategoryID });

【讨论】:

    猜你喜欢
    • 2023-02-04
    • 1970-01-01
    • 2021-02-16
    • 2020-04-09
    • 1970-01-01
    • 1970-01-01
    • 2018-04-05
    • 2023-03-29
    相关资源
    最近更新 更多