实体框架6.2从一个DbContext复制许多副本到另一个DbContext [英] Entity Framework 6.2 copy many to many from one DbContext to another DbContext

查看:97
本文介绍了实体框架6.2从一个DbContext复制许多副本到另一个DbContext的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用MySQL之类的网络数据库时,DbContext应该短暂存在,但根据

When working with a network database such as MySQL, the DbContext should be short lived, but according to https://www.entityframeworktutorial.net/EntityFramework4.3/persistence-in-entity-framework.aspx the DbContext can be long lived when working with a local database, such as SQLite.

我的应用程序使用的DbContext寿命长,可以在HDD上使用SQLite,并且我想将USB上相同类型的SQLite数据库的多对多实体复制到另一个DbContext.

My app is using a long lived DbContext to work with SQLite on HDD and I want to copy many-to-many entities to another DbContext for the same type of SQLite database on USB.

我正在使用代码优先"方法.

I am using the Code-First approach.

public class Student
{
    public Student() 
    {
        this.Courses = new HashSet<Course>();
    }

    public int StudentId { get; set; }
    [Required]
    public string StudentName { get; set; }

    public virtual ICollection<Course> Courses { get; set; }
}

public class Course
{
    public Course()
    {
        this.Students = new HashSet<Student>();
    }

    public int CourseId { get; set; }
    public string CourseName { get; set; }

    public virtual ICollection<Student> Students { get; set; }
}

DbContextHDD包含学生StudentA,StudentB和StudentC以及课程Course1,Course2和Course3:

DbContextHDD contains students StudentA, StudentB and StudentC and courses Course1, Course2 and Course3:

StudentA attends Course1 and Course3
StudentB attends Course2 and Course3
StudentC attends Course1 and Course2

DbContextUSB没有学生,也没有课程.

DbContextUSB contains no students and no courses.

var courses = DbContextHDD.Courses.AsNoTracking();
List<Student> students = new List<Student>();
foreach(Course course in courses)
{
    foreach(Student student in course.Students)
    {
        if(!students.Any(s => s.StudentId == student.StudentId))
        {
            students.Add(student);
        }
    }
}
Debug.WriteLine(students.Count); // output: 3

Debug.WriteLine(DbContextUSB.Students.Local.Count); // output: 0
DbContextUSB.Students.AddRange(students);
Debug.WriteLine(DbContextUSB.Students.Local.Count); // output: 4
DbContextUSB.SaveChanges(); // exception: UNIQUE constraint failed

DbContextUSB.Courses.AddRange(courses);
DbContextUSB.SaveChanges();

在将3个唯一学生插入具有0个学生的DbSet中后,为什么会有4个学生(3个唯一和1个重复)?正确的方法是什么?

Why are there 4 students (3 unique and 1 duplicate) after I insert 3 unique students in to a DbSet with 0 students? What is the proper way to do this?

正如我所说,由于使用SQLite,我使用的DbContext寿命长.

As I said, I am using a long lived DbContext because I am working with SQLite.

推荐答案

首先,不要使用AsNoTracking:

var courses = DbContextHDD.Courses. ...

第二,Include所需数据:

var courses = DbContextHDD.Courses
    .Include(c => c.Students)
    .ToList();

第三,将课程添加到其他上下文中:

Third, add the courses to the other context:

DbContextUSB.Courses.AddRange(courses);
DbContextUSB.SaveChanges();

您可能不相信,但本质上就是全部!

You may not believe it, but in essence that's all!

一个警告是,您应该在源上下文中禁用代理创建:

One caveat is that you should disable proxy creation in the source context:

DbContextHDD.Configuration.ProxyCreationEnabled = false;

否则,EF将创建代理对象,该代理对象引用了它们来自的上下文.它们不能附加到另一个上下文.

Otherwise EF creates proxy objects, which have a reference to the context they came from. They can't be attached to another context.

另一个问题是,可能有些学生没有上课.查询课程时,您会想念它们的.因此,您必须分别添加它们:

Another is that there may be students that don't attend courses. You'll miss them when querying courses. So you have to add them separately:

var lazyStudents = DbContextHDD.Students.Where(s => s.Courses.Count() == 0).ToList();
...
DbContextUSB.Students.AddRange(lazyStudents);
...
DbContextUSB.SaveChanges();

这为什么起作用?

  • 在没有跟踪的情况下,Entity Framework无法在以下位置检测到StudentA Course1与Course3中的学生相同.结果,StudentA 在Course3中,是一个新的Student实例.您最终将有6个学生,3个重复项(如果StudentName上没有唯一的索引可以防止这种情况).通过跟踪,EF可以检测 这两个课程都具有相同的Student实例.

  • Without tracking, Entity Framework can't detect that StudentA in Course1 is the same student as in Course3. As a consequence, StudentA in Course3 is a new Student instance. You'll end up having 6 students, 3 duplicates (if there's no unique index on StudentName preventing this). With tracking, EF does detect that both courses have the same Student instance.

在将实体添加到上下文时,EF也会标记为嵌套 尚未附加到上下文的实体为Added. 这就是为什么仅添加课程就足够了,这就是为什么EF不这样做的原因 当课程包含相同的学生实例时抱怨.

When adding an entity to a context, EF also marks nested entities as Added when they're not yet attached to the context. That's why it's enough to add courses only, and that's why EF doesn't complain when courses contain the same student instances.

由于添加的课程正确填充了它们的Students集合,因此EF还将在StudentCourse表中插入所需的联结记录.这在您的代码中没有发生(也许,或部分,请参阅稍后).

Since the added courses have their Students collections properly populated, EF also inserts the required junction records in the StudentCourse table. This didn't happen in your code (well maybe, or partly, see later).

现在为什么要招4个学生?

Now why did you get 4 students?

查看课程:

Course1 StudentA*, StudentC*
Course2 StudentB*, StudentC
Course3 StudentA , StudentB

由于AsNoTracking的所有学生都是不同的实例,但由于添加方式的原因,students中只有带标记的*学生.但这是棘手的部分.即使使用AsNoTracking(),Entity Framework也会对在一个查询中实现的相关实体执行关系修正.这意味着foreach(Course course in courses)循环会生成带有填充的Students集合的课程,每个学生在其Courses集合中都有一门课程.尤其是几乎不可能跟踪到底发生了什么.因为调试也会触发延迟加载,但是可以肯定的是,该行...

Because of AsNoTracking all student are different instances, but only the marked* students are in students because of how you add them. But here's the tricky part. Even with AsNoTracking(), Entity Framework executes relationship fixup with related entities that are materialized in one query. That means that the foreach(Course course in courses) loop produces courses with populated Students collections of which each student has one course in its Courses collection. It's almost impossible to keep track of what exactly happens, esp. because debugging also triggers lazy loading, but for sure, the line...

DbContextUSB.Students.AddRange(students);

还将他们嵌套的课程和他们的学生标记为Added,直到他们最终成为不同的实例.在这种情况下,最终结果是将另一个学生实例添加到缓存中.另外,创建了许多结点记录,但不一定是正确的结点记录.

also marks their nested courses and their students as Added as far as they ended up being different instances. The end result in this case is that one more student instance is added to the cache. Also, a number of junction records was created but not necessarily the correct ones.

结论是EF是克隆对象图的好工具,但是图必须正确填充,正确的关系且没有重复,并且应一次性添加.

The conclusion is that EF is a great tool for cloning object graphs, but the graph must be populated correctly, the right relationships and no duplicates, and should be added in one go.

这篇关于实体框架6.2从一个DbContext复制许多副本到另一个DbContext的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆