【问题标题】:EFCore eager loading issueEFCore 急切加载问题
【发布时间】:2021-01-16 14:42:00
【问题描述】:

我有点困惑为什么这在 EF Core 3.1.8 和 SQLite 中不起作用。我在具有 int 和 Guid 的两个实体之间有复合外键关系。当我尝试使用商店部分急切加载所有商店时,StoreSection 导航属性未返回任何结果。

我检查了创建的表,数据按预期保存。我检查了生成的优化 sql,当我直接运行它时,结果返回了我期望的结果。下面是一个演示该问题的控制台应用程序。

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;

namespace TestEfcore
{
    class Program
    {
        static void Main()
        {
            Guid userId = Guid.NewGuid();

            using (var context = new GroceryManagerContext())
            {
                var newStore = new Store
                {
                    Name = "Grocery World",
                    GroceryManagerUserId = userId
                };

                newStore.StoreSections.Add(new StoreSection() { Name = "Deli", GroceryManagerUserId = userId });

                newStore.StoreSections.Add(new StoreSection() { Name = "Produce", GroceryManagerUserId = userId });

                context.Add(newStore);
                context.SaveChanges();
            }
            
            using (var context = new GroceryManagerContext())
            {
                // Attempting to eager load store sections.
                var stores = context.Stores
                   .Include(s => s.StoreSections)
                   .Where(s => s.GroceryManagerUserId == userId)
                   .ToList();

                Console.WriteLine($"The user guid is {userId}.");

                foreach (var store in stores)
                {
                    Console.WriteLine($"There are {store.StoreSections.Count} store sections in {store.Name}.");
                }
            }
        }

        public class Store
        {
            public int StoreId { get; set; }

            public string Name { get; set; } = string.Empty;

            public Guid GroceryManagerUserId { get; set; }

            public List<StoreSection> StoreSections { get; } = new List<StoreSection>();
        }

        public class StoreSection
        {
            public int StoreSectionId { get; set; }

            public string Name { get; set; } = string.Empty;

            public Guid GroceryManagerUserId { get; set; }

            public int StoreId { get; set; }
            public Store Store { get; set; } = new Store();
        }

        public class GroceryManagerContext : DbContext
        {
            public DbSet<Store>? Stores { get; set; }

            public DbSet<StoreSection>? StoreSections { get; set; }

            protected override void OnModelCreating(ModelBuilder builder)
            {
                if (builder == null)
                    throw new ArgumentNullException(nameof(builder));

                base.OnModelCreating(builder);

                builder.Entity<Store>()
                    .HasIndex(s => new { s.Name, s.GroceryManagerUserId })
                    .IsUnique();

                builder.Entity<StoreSection>()
                    .HasIndex(ss => new { ss.Name, ss.GroceryManagerUserId })
                    .IsUnique();

                builder.Entity<StoreSection>()
                    .HasOne(ss => ss.Store)
                    .WithMany(s => s.StoreSections)
                    .HasForeignKey(ss => new { ss.StoreId, ss.GroceryManagerUserId })
                    .HasPrincipalKey(s => new { s.StoreId, s.GroceryManagerUserId })
                    .IsRequired();
            }

            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                optionsBuilder.UseSqlite(@"Data Source=D:\src\TestEfcore\TestEfcore\GroceryManager.db");
            }
        }
    }
}

【问题讨论】:

  • 1) 您是否在SQL Server 中建立了Store 表和StoreSection 表之间的关系? 2) 您是自己创建 dbcontext 还是 EFCore 为您自动生成的 dbcontext 和模型?

标签: c# sqlite entity-framework-core


【解决方案1】:

StoreSections 导航属性不是预先加载的,因为您正在使用 new 运算符初始化 StoreSection 实体上的 Store 属性。

这会导致 StoreSection.StoreId 属性被保存为 0,从而阻止创建关联

您需要更改 StoreSection 实体以删除初始化以解决问题:

public class StoreSection
{
    public int StoreSectionId { get; set; }

    public string Name { get; set; } = string.Empty;

    public Guid GroceryManagerUserId { get; set; }

    public int StoreId { get; set; }
    public Store Store { get; set; } // initialization has been removed here
}

【讨论】:

  • 如果你想为每个创建的StoreSection 创建一个Store,我建议编写一个静态工厂方法。由于 EF 在从数据库加载实例时取决于默认构造函数的行为。
  • 虽然我非常感谢您的反馈,而且新运营商似乎确实是罪魁祸首,但我仍然有点困惑为什么这会导致问题。我只是在有和没有 new Store() 的情况下重新运行了测试程序,存储到数据库中的内容是相同的:1 Deli DD44F8A2-CA57-4877-A34B-AB59E9A0C3B7 12 Produce DD44F8A2-CA57-4877-A34B-AB59E9A0C3B7 11 Deli 57CCEDA5-72E2-4606-86B4-92916F868756 12 Produce 57CCEDA5-72E2-4606-86B4-92916F868756 1
  • 好的,经过更多研究后,我发现这是导航属性的特定限制。以下是更多信息以及如何解决可空性:docs.microsoft.com/en-us/ef/core/miscellaneous/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多