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

如何阻止实体框架尝试保存/插入子对象?

宋望
2023-03-14

当我使用实体框架保存实体时,我自然假设它只会尝试保存指定的实体。但是,它也在尝试保存该实体的子实体。这会导致各种完整性问题。如何强制EF只保存我要保存的实体并因此忽略所有子对象?

如果我手动将属性设置为null,我会收到一个错误“操作失败:无法更改关系,因为一个或多个外键属性不可为空。”这是非常适得其反的,因为我专门将子对象设置为null,因此EF不会管它。

为什么我不想保存/插入子对象?

由于这在评论中被反复讨论,我将给出一些理由来说明为什么我希望我的子对象不受影响。

在我正在构建的应用程序中,EF对象模型不是从数据库加载的,而是用作我在解析平面文件时填充的数据对象。对于子对象,其中许多是指定义父表的各种属性的查找表。例如,主实体的地理位置。

由于我自己填充了这些对象,EF假设这些是新对象,需要与父对象一起插入。但是,这些定义已经存在,我不想在数据库中创建重复的定义。我只使用EF对象进行查找,并在主表实体中填充外键。

即使子对象是真实数据,我也需要先保存父对象并获取主键,否则EF似乎会把事情弄得一团糟。希望这能给出一些解释。

共有3个答案

宇文灿
2023-03-14

如果只想存储对父对象的更改,而不想存储对其任何子对象的更改,那么为什么不只执行以下操作:

using (var ctx = new MyContext())
{
    ctx.Parents.Attach(parent);
    ctx.Entry(parent).State = EntityState.Added;  // or EntityState.Modified
    ctx.SaveChanges();
}

第一行将父对象及其从属子对象的整个图形附加到处于未更改状态的上下文中。

第二行仅更改父对象的状态,使其子对象处于未更改状态。

请注意,我使用了一个新创建的上下文,这样可以避免保存对数据库的任何其他更改。

东门茂实
2023-03-14

长话短说:使用外键可以节省时间。

假设您有一个学校实体和一个城市实体,这是一种多对一关系,其中一个城市有多所学校,而一所学校属于一个城市。并假设这些城市已经存在于查找表中,因此您不希望在插入新学校时再次插入它们。

最初,您可能会这样定义您的实体:

public class City
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class School
{
    public int Id { get; set; }
    public string Name { get; set; }

    [Required]
    public City City { get; set; }
}

您可以这样插入学校(假设您已经将城市属性分配给了新项):

public School Insert(School newItem)
{
    using (var context = new DatabaseContext())
    {
        context.Set<School>().Add(newItem);
        // use the following statement so that City won't be inserted
        context.Entry(newItem.City).State = EntityState.Unchanged;
        context.SaveChanges();
        return newItem;
    }
}

在这种情况下,上述方法可能会完美地工作,但是,我确实更喜欢对我来说更清晰和灵活的外键方法。请参阅下面更新的解决方案:

public class City
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class School
{
    public int Id { get; set; }
    public string Name { get; set; }

    [ForeignKey("City_Id")]
    public City City { get; set; }

    [Required]
    public int City_Id { get; set; }
}

这样,您可以明确定义学校具有外键City\u Id,并且它引用城市实体。因此,在加入学校时,你可以:

    public School Insert(School newItem, int cityId)
    {
        if(cityId <= 0)
        {
            throw new Exception("City ID no provided");
        }

        newItem.City = null;
        newItem.City_Id = cityId;

        using (var context = new DatabaseContext())
        {
            context.Set<School>().Add(newItem);
            context.SaveChanges();
            return newItem;
        }
    }

在这种情况下,您可以显式指定新记录的City\u Id,并从图中删除该城市,这样EF就不会费心将其与学校一起添加到上下文中。

虽然在第一印象中,外键方法似乎更复杂,但相信我,当涉及到插入多对多关系时,这种心态会为您节省很多时间(想象您有一个学校和学生关系,而学生有一个城市属性)等等。

希望这对你有帮助。

魏明亮
2023-03-14

据我所知,你有两个选择。

将所有子对象设为Null,这将确保EF不会添加任何内容。它也不会从数据库中删除任何内容。

使用以下代码将子对象设置为与上下文分离

 context.Entry(yourObject).State = EntityState.Detached

请注意,您不能分离列表集合。您必须循环查看列表,并按如下方式分离列表中的每个项目

foreach (var item in properties)
{
     db.Entry(item).State = EntityState.Detached;
}
 类似资料:
  • 我有一个地址对象,它具有城市属性。创建要通过EF6插入的全新地址时,我会填写所有必需的基本地址属性(地址行1、邮政编码等),但我不需要完全水合的城市实例,只要它具有如下ID: 当我尝试插入我的地址时,它也会尝试对城市的属性进行验证,但我不想对城市进行任何CRUD,因为我只需要它的ID。 我发现了下面的问题,它引导我从DbContext中分离条目,这样EF就不会试图对所述对象执行CRUD: 如何阻止

  • 我目前正在使用实体框架来建立一个论坛使用ASP。NET MVC I有3个主要模型[类别]1--*[论坛]1--*[帖子] 如果我有5个类别,每个类别有4个论坛,每个论坛有1000个帖子,我只想显示类别,当我这样做的时候,这意味着我也选择了20000个帖子?? 因为每个类别对象都有一个列表

  • 为什么EF首先插入具有依赖关系的子对象(PersonnelWorkRecord),然后插入它所依赖的对象(TimesheetActivity)。还有,在纠正这一点上,我有什么选择? 我不确定我是否理解Entity Framework为什么/如何按顺序插入对象,但下面是我用来插入父对象和几个子对象的代码。 运行代码时,PersonnelWorkRecord(TimesheetActivityID)上

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

  • 如何在不禁用任何当前打开的帧的情况下显示 JOption 窗格? 我有一个带有开始和停止按钮的JFrame。在任何时候,我都希望用户能够按下主框架上的“停止”按钮,以停止辅助线程(由开始按钮启动)。 但是,第二个正在运行的线程有时会打开一个 JOption 面板。当它这样做时,主框架被禁用,用户无法按下停止按钮。 (当它连续打开多个JOptionPane时,试图阻止它会非常令人沮丧)。 我试过了

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