Entity Framework Core使用断开模式的实体图(Entity Graph)
在上一章中,您学习了ChangeTracker如何自动更改所连接场景中每个实体的EntityState。在这里,您将了解Entity Framework Core中断开连接的实体图的根实体和子实体上不同方法的行为。
实体框架核心提供了以下不同方法,这些方法不仅将实体附加到上下文,而且还更改了断开连接的实体图中每个实体的EntityState:
- Attach()
- Entry()
- Add()
- Update()
- Remove()
让我们看看以上方法如何更改Entity Framework Core 2.x中实体图中的每个实体的EntityState。
Attach()
DbContext.Attach()和DbSet.Attach()方法将附加指定的断开连接的实体图并开始对其进行跟踪。它们返回EntityEntry的实例,该实例用于分配适当的EntityState。
下面的示例演示DbContext.Attach()方法在图形中每个实体的EntityState上的行为。
public static void Main()
{
var stud = new Student() { //Root entity (empty key)
Name = "Bill",
Address = new StudentAddress() //Child entity (with key value)
{
StudentAddressId = 1,
City = "Seattle",
Country = "USA"
},
StudentCourses = new List<StudentCourse>() {
new StudentCourse(){ Course = new Course(){ CourseName = "Machine Language" } },//Child entity (empty key)
new StudentCourse(){ Course = new Course(){ CourseId = 2 } } //Child entity (with key value)
}
};
var context = new SchoolContext();
context.Attach(stud).State = EntityState.Added;
DisplayStates(context.ChangeTracker.Entries());
}
private static void DisplayStates(IEnumerable<EntityEntry> entries)
{
foreach (var entry in entries)
{
Console.WriteLine($"Entity: {entry.Entity.GetType().Name},
State: {entry.State.ToString()} ");
}
}
Output:
Entity: Student, State: Added
Entity: StudentAddress, State: Unchanged
Entity: StudentCourse, State: Added
Entity: Course, State: Added
Entity: StudentCourse, State: Added
Entity: Course, State: Unchanged
在上面的示例中,stud是学生实体图的实例,其中包括对StudentAddress和StudentCourse实体的引用。 context.Attach(stud).State = EntityState.Added会将图钉实体图附加到上下文,并为其设置Added状态。
Attach()方法将添加的EntityState设置为根实体(在本例中为Student),无论其是否包含Key值。如果子实体包含键值,则它将被标记为“不变”,否则将被标记为“已添加”。上面示例的输出显示,学生实体具有Added EntityState,具有非空键值的子实体具有Unchanged EntityState,而具有空键值的子实体具有Added状态。
下表列出了在为断开连接的实体图设置其他EntityState时,Attach(方法的行为。
Attach() | 具有键值的根实体 | 具有空值或CLR默认值的根实体 | 具有键值的子实体 | 空实体或CLR默认值的子实体 |
---|---|---|---|---|
context.Attach(entityGraph).State = EntityState.Added | Added | Added | Unchanged | Added |
context.Attach(entityGraph).State = EntityState.Modified | Modified | Exception | Unchanged | Added |
context.Attach(entityGraph).State = EntityState.Deleted | Deleted | Exception | Unchanged | Added |
## Entry()
与以前的EF 6.x相比,DbContext.Entry(方法在Entity Framework Core中的行为有所不同。考虑以下示例:
var student = new Student() { //Root entity (empty key)
Name = "Bill",
Address = new StudentAddress() //Child entity (with key value)
{
StudentAddressId = 1,
City = "Seattle",
Country = "USA"
},
StudentCourses = new List<StudentCourse>() {
new StudentCourse(){ Course = new Course(){ CourseName="Machine Language" } },//Child entity (empty key)
new StudentCourse(){ Course = new Course(){ CourseId=2 } } //Child entity (with key value)
}
};
var context = new SchoolContext();
context.Entry(student).State = EntityState.Modified;
DisplayStates(context.ChangeTracker.Entries());
Output:
Entity: Student, State: Modified
在上面的示例中,context.Entry(student).State = EntityState.Modified将实体附加到上下文,并将指定的EntityState(在本例中为Modified)应用于根实体,而不管其是否包含Key属性值或不。它会忽略图中的所有子实体,并且不会附加或设置其EntityState。
下表列出了DbContext.Entry(方法的不同行为。
使用Entry()设置EntityState | 具有键值的根实体 | 具有空值或CLR默认值的根实体 | 有/没有键值的子实体 |
---|---|---|---|
context.Entry(entityGraph).State = EntityState.Added | Added | Added | Ignored |
context.Entry(entityGraph).State = EntityState.Modified | Modified | Modified | Ignored |
context.Entry(entityGraph).State = EntityState.Deleted | Deleted | Deleted | Ignored |
Add()
DbContext.Add和DbSet.Add方法将实体图附加到上下文,并将“ Added EntityState”设置为根和子实体,而不管它们是否具有键值。
var student = new Student() { //Root entity (with key value)
StudentId = 1,
Name = "Bill",
Address = new StudentAddress() //Child entity (with key value)
{
StudentAddressId = 1,
City = "Seattle",
Country = "USA"
},
StudentCourses = new List<StudentCourse>() {
new StudentCourse(){ Course = new Course(){ CourseName="Machine Language" } },//Child entity (empty key)
new StudentCourse(){ Course = new Course(){ CourseId=2 } } //Child entity (with key value)
}
};
var context = new SchoolContext();
context.Students.Add(student);
DisplayStates(context.ChangeTracker.Entries());
Output:
输出:
Entity: Student, State: Added
Entity: StudentAddress, State: Added
Entity: StudentCourse, State: Added
Entity: Course, State: Added
Entity: StudentCourse, State: Added
Entity: Course, State: Added
下表列出了使用DbContext.Add或DbSet.Add方法的图形中每个实体的可能EntityState。
方法 | 具有/不具有键值的根实体 | 有/没有键值的子实体 |
---|---|---|
DbContext.Add(entityGraph) or DbSet.Add(entityGraph) | Added | Added |
Update()
DbContext.Update(和DbSet.Update(方法将实体图附加到上下文,并根据图中是否包含键属性值来设置图中每个实体的EntityState。考虑以下示例。
var student = new Student() { //Root entity (with key value)
StudentId = 1,
Name = "Bill",
Address = new StudentAddress() //Child entity (with key value)
{
StudentAddressId = 1,
City = "Seattle",
Country = "USA"
},
StudentCourses = new List<StudentCourse>() {
new StudentCourse(){ Course = new Course(){ CourseName="Machine Language" } },//Child entity (empty key)
new StudentCourse(){ Course = new Course(){ CourseId=2 } } //Child entity (with key value)
}
};
var context = new SchoolContext();
context.Update(student);
DisplayStates(context.ChangeTracker.Entries());
Output:
Entity: Student, State: Modified
Entity: StudentAddress, State: Modified
Entity: StudentCourse, State: Added
Entity: Course, State: Added
Entity: StudentCourse, State: Added
Entity: Course, State: Modified
在上面的示例中,Update(方法将Modified状态应用于包含非空键属性值的实体,并将Added状态应用于包含空或默认CLR键值的实体,而不管它们是根实体还是子实体。
Update() | 具有键值的根实体 | 具有空值或CLR默认值的根实体 | 具有键值的子实体 | 空键值的子实体 |
---|---|---|---|---|
DbContext.Update(entityGraph) or DbSet.Update(entityGraph) | Modified | Added | Modified | Added |
Remove()
DbContext.Remove(和DbSet.Remove(方法将Deleted EntityState设置为根实体。
var student = new Student() { //Root entity (with key value)
StudentId = 1,
Name = "Bill",
Address = new StudentAddress() //Child entity (with key value)
{
StudentAddressId = 1,
City = "Seattle",
Country = "USA"
},
StudentCourses = new List<StudentCourse>() {
new StudentCourse(){ Course = new Course(){ CourseName="Machine Language" } },//Child entity (empty key)
new StudentCourse(){ Course = new Course(){ CourseId=2 } } //Child entity (with key value)
}
};
var context = new SchoolContext();
context.Remove(student);
DisplayStates(context.ChangeTracker.Entries());
Output:
Entity: Student, State: Deleted
Entity: StudentAddress, State: Unchanged
Entity: StudentCourse, State: Added
Entity: Course, State: Added
Entity: StudentCourse, State: Added
Entity: Course, State: Unchanged
下表列出了每个实体的EntityState上Remove(方法的行为。
Remove() | 具有键值的根实体 | 具有空值或CLR默认值的根实体 | 具有键值的子实体 | 空键值的子实体 |
---|---|---|---|---|
DbContext.Remove(entityGraph) or DbSet.Remove(entityGraph) | Deleted | Exception | Unchanged | Added |
因此,在EF Core中使用上述方法时要小心。
在下一章中,将学习有关如何处理实体图中每个实体的ChangeTracker.TrackGraph(方法。