当前位置: 首页 > 知识库问答 >
问题:

实体框架以错误的顺序插入子对象

乜清野
2023-03-14

为什么EF首先插入具有依赖关系的子对象(PersonnelWorkRecord),然后插入它所依赖的对象(TimesheetActivity)。还有,在纠正这一点上,我有什么选择?

我不确定我是否理解Entity Framework为什么/如何按顺序插入对象,但下面是我用来插入父对象和几个子对象的代码。

using (var db = new DataContext(user))
{
     timesheet.State = State.Added;
     timesheet.SequenceNumber = newSequenceNumber;
     this.PrepareAuditFields(timesheet);

     //To stop EF from trying to add all child objects remove them from the timehseets object.
     timesheet = RemoveChildObjects(timesheet, db);

     //Add the Timesheet object to the database context, and save.
     db.Timesheets.Add(timesheet);
     result = db.SaveChanges() > 0;
}

运行代码时,PersonnelWorkRecord(TimesheetActivityID)上出现SQL外键冲突,因为我尚未添加活动(请参阅跟踪)。

exec sp_executesql N'insert [dbo].[Timesheets]([ProjectID], [TimesheetStatusID], ...
exec sp_executesql N'insert [dbo].[PersonnelWorkdays]([TimesheetID], [PersonnelID], ...
exec sp_executesql N'insert [dbo].[PersonnelWorkRecords]([PersonnelWorkdayID],[TimesheetActivityID], ...
modelBuilder.Entity<PersonnelWorkday>().HasRequired(pwd => pwd.Personnel).WithMany(p => p.PersonnelWorkdays).HasForeignKey(pwd => pwd.PersonnelID).WillCascadeOnDelete(false);
modelBuilder.Entity<PersonnelWorkday>().HasRequired(pwd => pwd.Timesheet).WithMany(t => t.PersonnelWorkdays).HasForeignKey(pwd => pwd.TimesheetID).WillCascadeOnDelete(false);
modelBuilder.Entity<PersonnelWorkRecord>().HasRequired(pwr => pwr.PersonnelWorkday).WithMany(pwd => pwd.PersonnelWorkRecords).HasForeignKey(pwr => pwr.PersonnelWorkdayID).WillCascadeOnDelete(false);
modelBuilder.Entity<PersonnelWorkRecord>().HasRequired(pwr => pwr.TimesheetActivity).WithMany(ta => ta.PersonnelWorkRecords).HasForeignKey(pwr => pwr.TimesheetActivityID).WillCascadeOnDelete(false);
modelBuilder.Entity<TimesheetActivity>().HasRequired(ta => ta.ProjectActivity).WithMany(a => a.TimesheetActivities).HasForeignKey(ta => ta.ProjectActivityCodeID).WillCascadeOnDelete(false);
modelBuilder.Entity<TimesheetActivity>().HasOptional(ta => ta.Facility).WithMany(f => f.TimesheetActivities).HasForeignKey(tf => tf.FacilityID).WillCascadeOnDelete(false);
modelBuilder.Entity<TimesheetActivity>().HasRequired(ta => ta.Timesheet).WithMany(t => t.TimesheetActivities).HasForeignKey(ta => ta.TimesheetID).WillCascadeOnDelete(false);

下面是子对象方法的代码。我添加了这个方法,以从时间表的子对象相关对象中删除非外键的对象。例如,我有一个Crew对象,但我也有一个CrewID外键,所以我将Crew设置为null,以便EF不会尝试插入它,因为它已经存在。

private Timesheet RemoveChildObjects(Timesheet timesheet, DataContext db)
{
        timesheet.Crew = null;
        timesheet.Foreman = null;
        timesheet.Location = null;
        timesheet.Project = null;
        timesheet.SigningProjectManager = null;
        timesheet.TimesheetStatus = null;
        timesheet.Creator = null;
        timesheet.Modifier = null;

        if (timesheet.TimesheetActivities != null)
        {
            foreach (TimesheetActivity tsa in timesheet.TimesheetActivities)
            {
                tsa.Creator = null;
                if (tsa.EquipmentWorkRecords != null)
                {
                    tsa.EquipmentWorkRecords = RemoveChildObjects(tsa.EquipmentWorkRecords, db);
                }
                tsa.Facility = null;
                tsa.Modifier = null;
                if (tsa.PersonnelWorkRecords != null)
                {
                    tsa.PersonnelWorkRecords = RemoveChildObjects(tsa.PersonnelWorkRecords, db);
                }
                tsa.ProjectActivity = null;
                tsa.Structures = null;
                tsa.Timesheet = null;
            }
        }

        if (timesheet.TimesheetEquipment != null)
        {
            foreach (TimesheetEquipment te in timesheet.TimesheetEquipment)
            {
                te.Equipment = null;
                te.Timesheet = null;
            }
        }

        if (timesheet.EquipmentWorkdays != null)
        {
            timesheet.EquipmentWorkdays = RemoveChildObjects(timesheet.EquipmentWorkdays, true, db);
        }

        if (timesheet.TimesheetPersonnel != null)
        {
            foreach (TimesheetPersonnel tp in timesheet.TimesheetPersonnel)
            {
                tp.Personnel = null;
                tp.PersonnelWorkday = null;
                if (tp.PersonnelWorkday != null)
                {
                    tp.PersonnelWorkday = RemoveChildObjects(tp.PersonnelWorkday, db);
                }
                tp.Timesheet = null;
            }
        }

        if (timesheet.PersonnelWorkdays != null)
        {
            timesheet.PersonnelWorkdays = RemoveChildObjects(timesheet.PersonnelWorkdays, true, db);
        }

        return timesheet;
    }

INSERT语句与外键约束“FK\U PersonnelWorkRecords\U TimesheetActivities”冲突。冲突发生在数据库“VPMTEST\u GC”、表“dbo.TimesheetActivities”、列“TimesheetActivityID”中。该语句已终止。

请让我知道,如果有什么我可以张贴来帮助描述我的问题。我在google/SO搜索过答案,但到目前为止还没有可靠的答案,除非域模型设置不同,否则EF似乎无法确定插入对象的顺序?我无法更改大多数对象的结构,因为它们被另一个系统使用。我可以尝试更改我的EF调用,我宁愿不使用原始SQL,因为对象比我在这里发布的简化版本要广泛得多。

类似问题:自引用实体和插入顺序

共有2个答案

符风畔
2023-03-14

这可能不再有效,但是否可以选择使用事务并单独添加每个子对象?

注意:我认为Slauma的解决方案更完整,但是交易调用可能仍然是其他有类似问题的人的选择。

邢项禹
2023-03-14

在您的RemoveChildObjects方法中,我看到一行。。。

tsa.Timesheet = null;

因此,显然您正在将Timesheet的逆导航属性设置为null。您是否对个性化工作记录做了同样的事情。Timesheetactive人格化工作记录。人格化工作日,即您是否将这些属性设置为null以及在嵌套RemveChildObject方法中?

这可能是一个问题,因为从时间表到人员工作记录有两条不同的路径,即:

Timesheet -> TimesheetActivities -> PersonnelWorkRecords
Timesheet -> PersonnelWorkdays -> PersonnelWorkRecords

当您调用数据库时。时间表。添加(时间表)我相信EF将逐个遍历对象图中的每个分支,并确定路径上哪些相关对象(“节点”)是从属的,哪些是关系中的主体,以确定插入顺序<代码>时间表本身是其所有关系的主体,因此必须首先插入它。然后EF开始遍历其中一个集合<代码>时间表。时间表有效性或时间表。人员工作日。哪一个先来并不重要。显然,EF以时间表开始。人员工作日。(如果从时间表开始,则无法解决问题。时间表活动性(TimesheetActivity)也会出现相同的异常,但使用的是人事工作记录(PersonnelWorkRecord)。人事工作日(PersonnelWorkday)而不是人事工作记录(PersonnelWorkRecord)。时间表活动性(TimesheetActivity)<代码>人员工作日仅依赖于已插入的时间表。因此,也可以插入人员工作日。

然后EF继续使用PersonnelWorkday遍历。人员工作记录。关于人员工作记录的人员工作日依赖关系,再次没有问题,因为之前已经插入了人员工作日。但是,当EF遇到人事工作记录(PersonnelWorkRecord)的依赖项(TimesheetActivity)时,它会看到这个时间表(TimesheetActivity)为空(因为您已将其设置为空)。现在,它假设依赖关系仅由外键属性TimesheetActivityID描述,该属性必须引用现有记录。它插入PersonnelWorkRecord,这违反了外键约束。

如果人员工作记录。TimesheetActivity不为空EF将检测到此对象尚未插入,但它是人事工作记录的主体。因此,它可以确定此时间表活动必须插入到人员工作记录之前。

如果您没有将反向导航属性设置为null,或者至少没有将PersonnelWorkRecord中的两个导航属性设置为null,我希望您的代码能够正常工作。(将其他导航属性(如tsa.Creator、tsa.Facility等)设置为null应该不会有问题,因为数据库中确实已经存在这些相关对象,并且您已经为这些对象设置了正确的FK属性值。)

 类似资料:
  • 错误消息: System.InvalidOperationException:未找到具有固定名称“System.Data.SqlClient”得ADO.NET提供程序得实体框架提供程序.确保在应用程序配置文件的“Entity Framework”部分中注册了提供程序。有关详细信息,请参阅http://go.microsoft.com/fwlink/?LinkId=260882。

  • 当我使用实体框架保存实体时,我自然假设它只会尝试保存指定的实体。但是,它也在尝试保存该实体的子实体。这会导致各种完整性问题。如何强制EF只保存我要保存的实体并因此忽略所有子对象? 如果我手动将属性设置为null,我会收到一个错误“操作失败:无法更改关系,因为一个或多个外键属性不可为空。”这是非常适得其反的,因为我专门将子对象设置为null,因此EF不会管它。 为什么我不想保存/插入子对象? 由于这

  • 问题内容: 我有一个简单的问题,即与“ not in” SQL等效项有关的实体框架语法。本质上,我想将以下SQL语法转换为Entity Framework语法: 这是我用于查找单个记录的方法: 这是我要用于此的伪方法: 谁能给我指点一下条款区域的内容吗?我阅读了一些有关此的论坛,并提到了使用或的情况,但是没有一个例子非常合适。 问题答案: 快去… 这些将大致变成以下数据库查询: 或者 分别。

  • 实体框架5.0首先使用现有数据库编写代码。使用电动工具对类进行逆向工程。一切都很好。数据库有两个表。一个父母和一个孩子的外键返回到父母ID。ID都是带有自动增量的int。我添加了许多父母记录,现在想将孩子记录添加到特定的父母。我能看到的唯一方法是通过在父母表中搜索名称或其他属性并返回ID来找到适当的父母ID。然后在添加孩子时在外键属性中使用该ID。我不想设置新父母,所以这是将孩子添加到现有父母的唯

  • 问题内容: 我正在将子系统从 NHibernate 移植到 Entity Framework, 并希望看到将以下查询移植到 EF 的最佳方法。 帐户余额类别为: 该表是: 示例数据是(使用数字ID可以更好地理解): 该 AccountBalanceByDate 实体持有某一天的账户余额。如果某天没有交易,则该天将没有 AccountBalanceByDate ,我们应该查看前几天以查看该帐户的余额

  • 我正在使用和。 我正在努力获取子对象的子对象来填充。下面是我的课。 应用程序类; 子类: ChildRelationshipType类: 存储库中GetAll方法的一部分,以返回所有应用程序: 子类包含对ChildRelationshipType类的引用。要处理应用程序的子级,我需要这样做: 我在这里得到一个错误,即对象上下文已经关闭。 如何指定每个子对象必须包括对象,就像我上面所做的那样?