猿教程 Logo

使用DbContext更新实体集

阿里云服务器,每月低至7.8元,项目演示即建站必备,比腾讯云更便宜,并且不需学生认证,从此链接购买有效:去购买

在断开连接的场景中更新一个实体图是一项复杂的任务。

在断开连接时很容易添加一个新的实体图,但是更新一个实体图需要精心设计的考虑。

问题在断开连接的情况下,更新一个实体图的上下文不知道在客户端上执行什么操作。

根据下图,新的环境不知道每个实体的状态:


在调用上下文的SaveChages方法之前(),我们需要确定实体图中每个实体的实体状态。

有几种不同的模式来识别实体状态,这是我们在设计数据层时需要考虑的。



断开连接场景识别实体状态

在断开连接的场景中有几种方式(如下所示)来识别一个实体:

  1. 使用PrimaryKey(Id)作为一个实体的属性

  2. 每个实体类型都包含一个State属性


1)一个实体使用PrimaryKey属性:

您可以使用PrimaryKey(Id)作为每个实体的属性来确定其实体状态。

但是,你必须遵守以下使用规则:

  1. 每种实体类必须有Id属性(PK)

  2. Id属性的默认值应该是0


上面的图中可以看到,客户端获取Standard 和Teacher 实体图并做一些修改操作,然后将其发送给上下文2保存更改。

在断开连接的情况下,context2不知道每个实体的状态。

它必须通过StandardId来确定Standard实体的状态,通过使用TeacherId属性来确定Teacher实体的状态。

如果StandardId TeacherID值为零,这意味着它是一个新的实体,如果它不为零则为修改后的实体。

在上面的图中,Standard ,Teacher1,和Teacher2有一个非零的Id属性值所以他们的状态被标记为Modified,而老师3 Id为零所以它的状态被标记为Added。

如下的代码片段所示使用Id属性来标记一个实体状态:

Standard disconnectedStandard = null;
using (var context = new SchoolDBEntities())
{
    context.Configuration.ProxyCreationEnabled = false;
    disconnectedStandard = context.Standards.Where(s => s.StandardId == 58).Include(s => s.Teachers).FirstOrDefault<Standard>();
}
//Update Standard in disconnected mode
disconnectedStandard.StandardName = "Edited Standard Name";
            
//Update teachers collection by editing first teacher and adding new teacher
disconnectedStandard.Teachers.ElementAt(0).TeacherName = "Edited Teacher Name";
disconnectedStandard.Teachers.Add(new Teacher() { TeacherName = "New Teacher", StandardId = disconnectedStandard.StandardId });
using (var newContext = new SchoolDBEntities())
{
    //mark standard based on StandardId
    newContext.Entry(disconnectedStandard).State = disconnectedStandard.StandardId == 0 ? EntityState.Added : EntityState.Modified;
    //mark teacher based on StandardId
    foreach (Teacher tchr in disconnectedStandard.Teachers)
        newContext.Entry(tchr).State = tchr.TeacherId == 0 ? EntityState.Added : EntityState.Modified;
                
                
    newContext.SaveChanges();
}

优点:

不需要额外的编码/处理来确定实体状态。

良好的性能。

缺点:

每个实体类型都需要有一个Id属性。它不能确定没有Id属性的实体的状态。

不能识别一个没有修改过的实体。如果一个实体并没有修改过,该实体也会被标记为Modified。所以,这样将会导致执行了一步多余的数据修改操作。

不能处理删除实体的场景。它需要单独处理删除。

2)每个实体类型都包含一个State属性

确定实体状态的另一种方法是,每一个实体类型都加一个状态属性,并且在断开连接场景中,手动设置一个合适的状态。

然后我们只需要在调用SaveChanges之前,设置实体状态的状态属性。

例子:

首先,创建一个包含枚举属性ObjectState的接口IEntityState:

interface IEntityObjectState
{
    EntityObjectState ObjectState { get; set; }
}
public enum EntityObjectState
{ 
    Added,
    Modified,
    Deleted,
    Unchanged
}

现在,每个实体类都实现一个IEntityObjectState接口,例如Standard 和Teacher 实体,如下所示:

public partial class Standard:IEntityObjectState
{
    public Standard()
    {
        this.Students = new HashSet<Student>();
        this.Teachers = new HashSet<Teacher>();
    }
    
    public int StandardId { get; set; }
    public string StandardName { get; set; }
    public string Description { get; set; }
    
    public virtual ICollection<Student> Students { get; set; }
    public virtual ICollection<Teacher> Teachers { get; set; }
    [NotMapped]
    public EntityObjectState ObjectState
    {
        get;
        set;
    }
}
public partial class Teacher:IEntityObjectState
{
    public Teacher()
    {
        this.Courses = new HashSet<Course>();
    }
    
    public int TeacherId { get; set; }
    public string TeacherName { get; set; }
    public Nullable<int> StandardId { get; set; }
    
    public virtual ICollection<Course> Courses { get; set; }
    public virtual Standard Standard { get; set; }
    [NotMapped]
    public EntityObjectState ObjectState
    {
        get;
        set;
    }
}

在断开连接模式下设置适当的状态:

Teacher existingTeacher = null;
using (var context = new SchoolDBEntities())
{
    context.Configuration.ProxyCreationEnabled = false;
    existingTeacher = context.Teachers.FirstOrDefault<Teacher>();
}
Standard disconnectedStandard = new Standard() { StandardName = "New Standard", ObjectState = EntityObjectState.Added };
existingTeacher.ObjectState = EntityObjectState.Modified;
//add existing teacher(in db) to standard
disconnectedStandard.Teachers.Add(existingTeacher);
//add new standard
disconnectedStandard.Teachers.Add(new Teacher() { TeacherName = "New teacher", StandardId = disconnectedStandard.StandardId, ObjectState = EntityObjectState.Added });

调用SaveChanges之前,设置实体状态。

using (var newContext = new SchoolDBEntities())
{
    //check the ObjectState property and mark appropriate EntityState 
    if (disconnectedStandard.ObjectState == EntityObjectState.Added)
        newContext.Entry(disconnectedStandard).State = System.Data.Entity.EntityState.Added;
    else if (disconnectedStandard.ObjectState == EntityObjectState.Modified)
        newContext.Entry(disconnectedStandard).State =System.Data.Entity.EntityState.Modified;
    else if (disconnectedStandard.ObjectState == EntityObjectState.Deleted)
        newContext.Entry(disconnectedStandard).State = System.Data.Entity.EntityState.Deleted;
    else
        newContext.Entry(disconnectedStandard).State = System.Data.Entity.EntityState.Unchanged;
    //check the ObjectState property of each teacher and mark appropriate EntityState 
    foreach (Teacher tchr in disconnectedStandard.Teachers)
    {
        if (tchr.ObjectState == EntityObjectState.Added)
            newContext.Entry(tchr).State = System.Data.Entity.EntityState.Added;
        else if (tchr.ObjectState == EntityObjectState.Modified)
            newContext.Entry(tchr).State = System.Data.Entity.EntityState.Modified;
        else if (tchr.ObjectState == EntityObjectState.Deleted)
            newContext.Entry(tchr).State = System.Data.Entity.EntityState.Deleted;
        else
            newContext.Entry(tchr).State = System.Data.Entity.EntityState.Unchanged;
    }
    //save changes
    newContext.SaveChanges();
}

优点:

不需要额外的编码/处理来确定实体状态

处理添加、修改、删除和不变状态正常

对没有修改过的实体,不会有额外的更新操作。

劣势:

需要在脱机模式下设置每个实体的状态。

所以在脱机模式下需要额外小心。


阿里云服务器,每月低至7.8元,项目演示即建站必备,比腾讯云更便宜,并且不需学生认证,从此链接购买有效: 去购买


版权声明:本站所有教程均为本站原创或翻译,转载请注明出处,请尊重他人劳动果实。请记住本站地址:www.yuanjiaocheng.net (猿教程) 作者:卿文刚
本文标题: C#环境
本文地址:http://www.yuanjiaocheng.net/entity/update-entity-graph.html