gpt4 book ai didi

c# - Entity Framework 6.2 将多对多从一个 DbContext 复制到另一个 DbContext

转载 作者:太空宇宙 更新时间:2023-11-03 12:00:40 26 4
gpt4 key购买 nike

当使用像 MySQL 这样的网络数据库时,DbContext 应该是短暂的,但是根据 https://www.entityframeworktutorial.net/EntityFramework4.3/persistence-in-entity-framework.aspx使用本地数据库(例如 SQLite)时,DbContext 可以长期存在。

我的应用程序使用长期存在的 DbContext 来处理 HDD 上的 SQLite,我想将多对多实体复制到另一个 DbContext 以用于 USB 上相同类型的 SQLite 数据库。

我正在使用代码优先方法。

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 以及类(class) Course1、Course2 和 Course3:

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

DbContextUSB 不包含学生和类(class)。

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 个重复的)?执行此操作的正确方法是什么?

正如我所说,我正在使用长期使用的 DbContext,因为我正在使用 SQLite。

最佳答案

首先,不要使用AsNoTracking:

var courses = DbContextHDD.Courses. ...

其次,包含需要的数据:

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

第三,将类(class)添加到其他上下文:

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

你可能不相信,但本质上就是这样!

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

DbContextHDD.Configuration.ProxyCreationEnabled = false;

否则 EF 会创建代理对象,这些对象具有对它们来自的上下文的引用。它们不能附加到另一个上下文。

另一个是可能会有学生不参加类(class)。查询类(class)时您会想念它们。所以你必须单独添加它们:

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

为什么会这样?

  • 如果没有跟踪, Entity Framework 无法检测到 StudentA 在Course1 与 Course3 中的学生相同。结果,学生 A在 Course3 中是一个新的 Student 实例。您最终将有 6 个学生,3 个重复(如果 StudentName 上没有唯一索引来阻止这种情况)。通过跟踪,EF 确实检测到两个类(class)都有相同的 Student 实例。

  • 将实体添加到上下文时,EF 还会标记嵌套当实体尚未附加到上下文时,将实体标记为 Added。这就是为什么只添加类(class)就足够了,这就是为什么EF不添加类(class)的原因当类(class)包含相同的学生实例时提示。

  • 由于添加的类(class)已正确填充其Students 集合,因此 EF 还将所需的联结记录插入到 StudentCourse 表中。这并没有发生在您的代码中(也许,或部分发生,稍后见)。

现在你为什么有 4 个学生?

查看类(class):

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

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

DbContextUSB.Students.AddRange(students);

还将它们的嵌套类(class)和它们的学生标记为已添加,因为它们最终是不同的实例。在这种情况下,最终结果是将另一个学生实例添加到缓存中。此外,创建了许多联结记录,但不一定是正确的。

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

关于c# - Entity Framework 6.2 将多对多从一个 DbContext 复制到另一个 DbContext,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57180967/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com