EF Core - 在 Sql 服务器 LocalDb 中创建意外的不完整行

EF Core - Creating Unexpected Incomplete Rows in Sql Server LocalDb

我有一个带有 EFCore 6.0.1 的新 VS2022 解决方案,网址为:https://github.com/RobBowman/BillByTime

我创建了一个数据库上下文来表示以下内容:

我在 VS2022 中使用了 EFCore Power Tools,并将它创建的图表与我的预期架构进行了比较 - 它们匹配!

这是数据库上下文 C#:

public class BillByTimeContext : DbContext
    {
        public BillByTimeContext(DbContextOptions<BillByTimeContext> options) : base(options)
        {
            RelationalDatabaseCreator databaseCreator =
                        (RelationalDatabaseCreator)this.Database.GetService<IDatabaseCreator>();
            databaseCreator.EnsureCreated();
        }

        public DbSet<ClientOrg>? ClientOrg { get; set; }
        public DbSet<Contract>? Contract { get; set; }
        public DbSet<PurchaseOrder>? PurchaseOrder { get; set; }
        public DbSet<Tenant> Tenant { get; set; }
        public DbSet<TenantManager>? TenantManager { get; set; }
        public DbSet<Timesheet>? Timesheet { get; set; }
        public DbSet<TimesheetHistory>? TimesheetHistory { get; set; }
        public DbSet<Worker>? Worker { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Tenant>()
                .HasIndex(x => x.Name)
                .IsUnique();

            modelBuilder.Entity<TenantManager>()
                .HasIndex(x => x.Email)
                .IsUnique();

            modelBuilder.Entity<Worker>()
                .HasMany(p => p.Contracts)
                .WithOne(t => t.Worker)
                .OnDelete(DeleteBehavior.NoAction);

            modelBuilder.Entity<Timesheet>()
                .HasMany(p => p.TimesheetHistories)
                .WithOne(t => t.Timesheet)
                .OnDelete(DeleteBehavior.NoAction);

            modelBuilder.Entity<Worker>()
                .HasMany(p => p.TimesheetHistories)
                .WithOne(t => t.Worker)
                .OnDelete(DeleteBehavior.NoAction);

            modelBuilder.Entity<Contract>()
                .Property(x => x.UnitCharge)
                .HasPrecision(10, 2);

            modelBuilder.Entity<PurchaseOrder>()
                .Property(x => x.Amount)
                .HasPrecision(10, 2);

            modelBuilder.Entity<Timesheet>()
                .Property(x => x.Monday)
                .HasPrecision(10, 2);
            modelBuilder.Entity<Timesheet>()
                .Property(x => x.Tuesday)
                .HasPrecision(10, 2);
            modelBuilder.Entity<Timesheet>()
                .Property(x => x.Wednesday)
                .HasPrecision(10, 2);
            modelBuilder.Entity<Timesheet>()
                .Property(x => x.Thursday)
                .HasPrecision(10, 2);
            modelBuilder.Entity<Timesheet>()
                .Property(x => x.Friday)
                .HasPrecision(10, 2);
            modelBuilder.Entity<Timesheet>()
                .Property(x => x.Saturday)
                .HasPrecision(10, 2);
            modelBuilder.Entity<Timesheet>()
                .Property(x => x.Sunday)
                .HasPrecision(10, 2);
        }

    }

我有一个调用以下 SeedData 方法的 xunit 测试:

public static void SeedData(BillByTimeContext context)
        {

            context.Database.EnsureCreated();

            var timesheetHistory = new TimesheetHistory
            {
                Timestamp = new DateTime(2022, 1, 7, 14, 30, 0),
                StatusId = TimesheetStatus.PendingApproval
            };

            var timesheet = new Timesheet
            {
                Monday = 1,
                Tuesday = .5M,
                Wednesday = 1,
                Thursday = 0,
                Friday = 1,
                WeekCommencingMonday = new DateTime(2022, 01, 03),
                TimesheetHistories = new List<TimesheetHistory> { timesheetHistory },
            };

            var purchaseOrder = new PurchaseOrder
            {
                DateIssued = DateTime.Now,
                Amount = 5462.5M,
                Timesheets = new List<Timesheet> { timesheet }
            };

            var clientManager = new ClientManager
            {
                FirstName = "Paul",
                LastName = "Arndel",
                Email = "p.arndel@brc.com",
                SmsNumber = "867428764",
                TimesheetHistories = new List<TimesheetHistory> { timesheetHistory }
            };

            var contract = new Contract
            {
                UnitId = ContractUnits.Days,
                UnitCharge = 550,
                Timesheets = new List<Timesheet> { timesheet }
            };

            var clientOrg = new ClientOrg
            {
                Name = "BRC",
                PurchaseOrders = new List<PurchaseOrder> { purchaseOrder },
                ClientManagers = new List<ClientManager> { clientManager },
                Contracts = new List<Contract> { contract },
                Timesheets = new List<Timesheet> { timesheet }
            };

            var tenantManager = new TenantManager
            {
                FirstName = "Rob",
                LastName = "Bowman",
                Email = "rob@biztalkers.com",
                ClientOrgs = new List<ClientOrg> { clientOrg }
            };

            var worker = new Worker
            {
                FirstName = "Fabio",
                LastName = "Capello",
                Email = "f.cap@ital.com",
                Contracts = new List<Contract> { contract },
                TimesheetHistories= new List<TimesheetHistory> { timesheetHistory }
            };

            var tenant = context.Tenant.FirstOrDefault(x => x.Name == "BizTalkers");
            if (tenant == null)
            {
                context.Tenant.Add(new Tenant
                {
                    Name = "BizTalkers",
                    TenantManagers = new List<TenantManager> { tenantManager },
                    Workers = new List<Worker> { worker }
                }); ;
            }

            context.SaveChanges();

如果我清除数据库中的所有数据并 运行 测试,它会给出以下错误:

---- Microsoft.Data.SqlClient.SqlException : Cannot insert duplicate key row in object 'dbo.Tenant' with unique index 'IX_Tenant_Name'. The duplicate key value is ()

谁能告诉我为什么要在“租户”table 中创建多个记录?

哦..刚刚注意到你 github 链接了你的整个项目 - 这很有用并证实了我的怀疑:

//worker class
public Tenant Tenant { get; set; } = new();


//tenantmanager class
public Tenant Tenant { get; set; } = new();

每次你创建一个新的工人,它就会创建一个新的租户。 EF 会将其视为必须保存的对象。您的图表至少包含 1 名工作人员(有 1 名租户,姓名为空)和 1 名租户经理(有 1 名租户,姓名为空),因此它看起来像

{Tenant "BizTalkers"} --has-some--> [ {Worker "Fabio"} --has-one--> { Tenant "" } ]
           \
            `--has-some--> [ {TenantManager "Rob"} --has-one--> { Tenant "" } ]

目前我不知道为什么 EFCPT 以这种方式生成实体(如果它确实生成了?)- 我的不知道。我不确定这是你打开的选项,还是数据库端的结果。如果我找到了什么,我会更新答案,但我相信现在你的问题是由这个。从这些道具中删除 = new() ,或者在初始化程序中将它们设置为 null ,或者以其他方式构建图形(将您创建的租户设置为您创建的工人的租户,覆盖 new() Tenant它有,而不是将您创建的工人设置为您创建的租户的工人之一)

此外,脚注,通常我会说“避免批发替换整个子实体列表”——这些集合通常 new 在 constructor/class prop 初始值设定项中作为空 HashSets 用于原因,您只需添加到它们而不是替换它们。如果你替换它们,更改跟踪器可能会认为你正在删除东西,并且会发生各种疯狂的事情