.SaveChanges()在实体框架中存储重复项

时间:2014-10-30 17:47:23

标签: asp.net-mvc entity-framework dbcontext

for ( int i = 0; i < libraryList.Count; i++)
{
    if (ModelState.IsValid)
    {
        context.Library.Add(libraryList[i]);
        context.SaveChanges();
    }
}

图书馆包含一个实体&#39;预定义目标&#39;已在DB中设置。因此,当上面的代码运行时,它存储了预定义目标的共同体。并为他们分配新的ID。

我读到我应该将现有实体附加到上下文中,但我不确定如何在我的场景中执行此操作。这些类看起来像这样:

class library
    int libraryID
    list<book> bks

class book
    int bookID
    list<importantdates> impdts

class importantdate
    int importantdateID
    predefinedgoal predfg
    int numberofresellers

class predefinedgoal
    int predefinedgoalID
    string description
    int daysfrompublication

我在ModelState.IsValid之后尝试过这样的事情,但我觉得我做错了:

var prdfgs= context.predefinedgoals.ToList();
foreach(var pg in prdfgs)
    context.predefinedgoals.Attach(pg);

3 个答案:

答案 0 :(得分:3)

这个答案将基于几个假设,但我已经多次看到这个确切的问题,这自然是我的回答。

我认为您正在做的是创建LibraryBookImportantDate个对象(并将它们之间的所有关系设置为好)。但是,在执行所有这些操作的过程中,您尝试在PreDefinedGoal个对象上设置ImportantDate导航属性,同时保留实际的int FK属性(即像PreDefinedGoalID)这样的东西,仍设置为0.当发生这种情况时,Entity Framework忽略了导航属性中包含的对象上有ID的事实,并假设您正在尝试创建此{{1}从头开始,就像你创建PreDefinedGoal对象(以及其他对象)一样。然后,它将创建一个ImportantDate对象,其数据与您实际尝试使用的数据完全相同,但它会将其创建为数据库中单独的重复记录。

您的问题的解决方案很简单:不要设置导航属性。只需将FK(PreDefinedGoal)设置为要与其挂钩的ImportantDate.PreDefinedGoalID对象的ID即可。当您这样做并保存它时,Entity Framework将根据该ID访问数据库以获取正确的对象,因此您将避免在数据库中出现重复的PreDefinedGoal个对象。

仅供参考:我是从Julie Lerman的MSDN posts中学到的。如果您要与EF合作,我强烈建议您阅读她的帖子和专栏。

答案 1 :(得分:1)

我处于相同的情况并找到了解决方法。这种解决方法的工作方式使我认为在这种情况下,EF应该为严重处理这种情况负责。

为了简化示例,我将发布一个带有一个对象的示例,它的导航属性。

public class Topic
{
   int Id { get; set; }
   public String Name { get; set; }
   public String Description { get; set; }
}

public class Course
{
   int Id { get; set; }
   public Topic Topic { get; set; }
   // additional properties don't matter now
}

请注意,没有任何外键或其他数据注释。 EF6将从此正确创建数据库架构,并推断 Id 是主键。

如果没有解决方法为现有主题添加新课程,则会创建一个带有新ID的新主题对象(覆盖它给出的ID!):

db.Courses.Add(course);
await db.SaveChangesAsync();

脑死亡解决方法:

course.topic = db.Topics.Find(course.topic.Id);
db.Courses.Add(course);
await db.SaveChangesAsync();

换句话说,如果直接从上下文加载主题,EF会将其识别为现有主题,不要再尝试添加。

更新:只需附加实体而不重新加载它:

db.Topics.Attach(course.topic);

但是,此设置会遇到更多问题,最好使用ForeignKey属性并在Course对象中包含TopicId。以下工作正常,但对我来说仍然看起来很荒谬:

    [ForeignKey("Topic")]
    public int TopicId { get; set; }

    [ForeignKey("TopicId")]
    public virtual Topic Topic { get; set; }

很想听到一个不太冗余的解决方案。

答案 2 :(得分:0)

为什么它在我的场景中存储重复项的答案是我在两个不同的类中执行任务 - 在每个类中使用不同的数据库上下文变量。

所以#1类就是我的问题,那就是我使用上下文#1保存到数据库的地方。在类#2中,我检索了所有PredefinedGoals并将它们添加到ImportantDates但是为了做到这一点,我创建了上下文#2。 ID和对象是相同的,但是从不同的上下文变量中检索。

我通过使用上下文变量#1检索类#1中的PredefinedGoals并将它们作为参数发送到类#2来解决它。