关于Entity Framework更新的几种方式以及可能遇到的问题(附加类型“Model”的实体失败,因为相同类型的其他实体已具有相同的主键值)在使用 "Attach" 方法或者将实体的状态设置为 "Unchanged" 或 "Modified" 时如果图形中的任何实体具有冲突键值,则可能会发生上述行为

在日常使用Entity Framework中,数据更新通常会用到。下面就简单封装了一个DBContext类

public partial class EFContext<T> : DbContext where T : class
    {
        public EFContext(): base("name=MyConnectionString")
        {
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            Database.SetInitializer<EFContext<T>> (null);
            modelBuilder.Configurations.Add(new MemberMap());
            modelBuilder.Configurations.Add(new RoleMap());
            base.OnModelCreating(modelBuilder);
        }

        
        public DbSet<T> Table { get; set; }

        public IQueryable<T> GetList(Expression<Func<T,bool>> where)
        {
            return this.Table.Where(where);
        }

        public void Update(T entity)
        {
            if (entity == null)
            {
                throw new ArgumentException("entity");
            }
            this.SaveChanges();
        }
    }
View Code

第一种更新方式,先通过Entity Framework从数据库中查找出一条记录(实体对象),然后修改实体对象的各个属性,最后调用Update方法

static void Main(string[] args)
{
    EFContext<Member> memberContext = new EFContext<Member>();
    var members = memberContext.GetList(m => true).ToList();
    var model = members.Find(m => m.Id == 3);

    //第一种更新方式
    model.Name = "猪八戒";
    model.Delete = false;
    memberContext.Update(model);
    Console.ReadKey();
}

运行程序前:

运行程序后:

 

上面的方式可以修改为下面方式,DbContext封装类中Update可以修改为如下的形式:

public void Update(T entity)
{
    if (entity == null)
    {
        throw new ArgumentException("entity");
    }
    this.Table.Attach(entity);
    this.Entry(entity).State = EntityState.Modified;
    this.SaveChanges();
}

 

static void Main(string[] args)
{
    EFContext<Member> memberContext = new EFContext<Member>();
    var members = memberContext.GetList(m => true).ToList();
    var model = members.Find(m => m.Id == 3);

    //第一种更新方式
    model.Name = "沙师弟";
    model.Delete = true;
    memberContext.Update(model);
    Console.ReadKey();
}

运行前:

运行后:

 

第二种方式是new一个对象,这个对象各个属性赋值,主键Id与数据库某条已存在的记录的Id相同

static void Main(string[] args)
{
    EFContext<Member> memberContext = new EFContext<Member>();
    var members = memberContext.GetList(m => true).ToList();
    //var model = members.Find(m => m.Id == 3);

    ////第一种更新方式
    //model.Name = "沙师弟";
    //model.Delete = true;
    //memberContext.Update(model);

    //第二种方式
    Member entity = new Member();
    entity.Id = 3;
    entity.Name = "李小龙";
    entity.Password = "lixiaolong";
    entity.Delete = false;
    entity.RoleId = 3;
    memberContext.Update(entity);

    Console.WriteLine("update complete.");
    Console.ReadKey();
}

运行程序,会抛出异常。

 

附加类型“Core.Member”的实体失败,因为相同类型的其他实体已具有相同的主键值。在使用 "Attach" 方法或者将实体的状态设置为 "Unchanged" 或 "Modified" 时如果图形中的任何实体具有冲突键值,则可能会发生上述行为。这可能是因为某些实体是新的并且尚未接收数据库生成的键值。在此情况下,使用 "Add" 方法或者 "Added" 实体状态跟踪该图形,然后将非新实体的状态相应设置为 "Unchanged" 或 "Modified"。

 

因为Attach的实体对象是通过new创建的,而不是通过Entity Framework从数据库中获取的,但实例的主键对应数据在数据库中存在,该实例而不存在于DBContext上下文中,尝试Attach会抛出异常。通过监视可以看到是未附加到DbContext中

修改Update方法如下:

public void Update(T entity)
{
    if (entity == null)
    {
        throw new ArgumentException("entity");
    }
    if (this.Entry(entity).State == EntityState.Detached)
    {
        HandleDetached(entity);
    }
    this.Table.Attach(entity);
    this.Entry(entity).State = EntityState.Modified;
    this.SaveChanges();
}

private bool HandleDetached(T entity)
{
    var objectContext = ((IObjectContextAdapter)this).ObjectContext;
    var entitySet = objectContext.CreateObjectSet<T>();
    var entityKey = objectContext.CreateEntityKey(entitySet.EntitySet.Name, entity);
    object foundSet;
    bool exists = objectContext.TryGetObjectByKey(entityKey, out foundSet);
    if (exists)
    {
        objectContext.Detach(foundSet); //从上下文中移除
    }
    return exists;
}

再次运行程序,没有问题

 

posted @ 2017-08-14 22:59  Jichan·Jong  阅读(5745)  评论(0编辑  收藏  举报