附加类型“penson”的实体失败,因为相同类型的其他实体已具有相同的主键值。在使用 "Attach" 方法或者将实体的状态设置为 "Unchanged" 或 "Modified" 时如果图形中的任何实体具有冲突键值,则可能会发生上述行为。这可能是因为某些实体是新的并且尚未接收数据库生成的键值。在此情况下,使用 "Add" 方法或者 "Added" 实体状态跟踪该图形,然后将非新实体的状态相应设置为 "Unchanged" 或 "Modified"。
context.Entry<Penson>(item).State = EntityState.Modified; //出错位置。 int result = context.SaveChanges();
请各位大神指点一下。 纠结了好半天了。
第一次更新数据没有问题。 但是第二次更新数据的时候就会出现上述错误。
查询过这个实体,你再这样放一个new的实体进去跟踪.他会有2个相同ID的对象,然后就报异常了
恩恩。 原因肯定是这样的。 只是不知道该怎么操作让之前的查询失效。。。 如果context.Dispose()的话 就不能在用了。
@刘剑_1989: 你干嘛要让原来的查询失效?
你既然用ef了.那除了插入数据,其它时候就不要new对象了.
要操作前线从上下文里把实体查询出来.再修改再保存,就不会有这些乱七八糟的问题了
@吴瑞祥: 结局问题办法。 查询单条数据的时候不从redis中查询 用EF查询。。。。。。。。。。。。。
感觉解决这个问题的办法真心好奇葩。。。
@刘剑_1989: 真的一点都不奇葩...你这是不理解实体追踪的意义..
你这种单条数据就算从ef里查,也只是查询的缓存,而不是数据库
context.Entry<Penson>(item).State = EntityState.Modified;
如果你非要用这种方式来更新,那我就不说了。
恕小弟才疏学浅 我这修改方法需要使用泛型。 有泛型还有其他的修改方式么?
@刘剑_1989: 更新是这样的,
T existing = Context.Set<T>().Find
如果 existing == null, Context.Set<T>().Add(item);
否则, 将 item 的值赋给 existing(不包括主键的值),
最后,Context.SaveChanges
@Launcher: 正解,修改更新数据到数据库有两种方式:连接模式下和断开模式下,楼主出问题的原因在因为连接模式,同一个上下文要缓存了两个同一个主键的对象,EF的上下文不允许这种情况出现。按照@Launcher 的提示,可以更新成功。代码如下
public bool UpdateEntity(UserInfo userInfo)
{
UserInfo user = context.Set<UserInfo>().Find(userInfo.UserId);
if(user!=null)
{
user.UserName = userInfo.UserName;
user.Password = userInfo.Password;
user.Email = userInfo.Email;
user.Phone = userInfo.Phone;
user.Remark = userInfo.UserName;
}
return context.SaveChanges() > 0;
}
@追忆似水流年: 微软的那种更新数据库的方式是在同一个上下文更新数据,更新完上下文就不在使用,会被回收,楼主出问题的原因在于是系统中上下文一直在使用(静态类创建的),一直是连接模式,这时会实体状态跟踪,所以可以使用Context.Set<T>().Find方法,得到对象实体,摆脱实体状态跟踪,来更新数据
@追忆似水流年: 这个Find方法会从上下文缓存通过主键寻找数据,找不到再去数据库中寻找,如果找到了,数据就会返回,这个返回的数据就不会缓存在上下文中,可以修改后提交到数据库中。如果上下文缓存和数据库中都找不到,就会返回null.
你这是被微软坑了。微软网站都是这么写的,可是你别这么用啊。
在使用泛型的情况下 还有其他的修改方式么? 请指点一下吧, 感激不尽。
@刘剑_1989: 你代码不全啊,谁知道你干啥了。
你代码全的话,估计楼上楼下这两位都能有办法,我就凑凑热闹,哈哈。
写错的我是知道,至于怎么才能写对,那....
@爱编程的大叔:
我是用的redis做了一层缓存。 第一次是 从缓存读取数据,修改字段。 提交保存EF, 成功后修改redis。 但是第二次 在从缓存读取数据 修改单独字段, 在提交保存EF的时候就报错了。
@刘剑_1989: 看了你回答楼下的,
你的context是永远不失效的?如果context不失效,你又何必用redis读?
context里面不是已经有一个了?
@爱编程的大叔:
补充问一个小白的问题。 EF是不是查询过一次数据之后 自动的把这些数据进行缓存处理了? 第二次查询的时候直接从缓存中读取?
@刘剑_1989: 你想想看咯,你看看你在Attach的时候出错,有没有产生数据库查询,这个你总应该懂得看吧?
@爱编程的大叔: 那我可不可以说 其实用EF+redis的结合本身就是错误的呢。
@刘剑_1989: 那我就不知道了,你EF明显不熟悉的样子,不知道你搞什么大项目,
居然连REDIS都出来了。这得有几千万的大项目啊。
REDIS+EF是没有错,问题看你怎么用。
你可别听人说EF性能差这事,
说这事的人99%是没有脑子的,还有1%的人是只干1个亿以上生意的人。
@爱编程的大叔: EF效率我不做评论。 说道熟与不熟 可能也仅仅是处于在 会用,但是不懂得怎么用效率更高。 因为最近一直自己在搞这些东西。 redis+EF的结合使用。 对这两样东西都不是很熟, 所以总是遇到各种奇葩的问题。
@刘剑_1989: 那我的建议是你尽可能先熟悉其中一种,比如EF。
public bool Update<T>(T entity) where T : class
{
Context.Set<T>().Attach(entity);
Context.Entry(entity).State = EntityState.Modified;
return Context.SaveChanges() > 0;
}
在 Context.Set<T>().Attach(entity); 的时候就直接报错了。
@刘剑_1989: 你的person表设置了主键没,是不是自动增长的
@人在江湖博客: 设置了主键 自增。
最简单的方法,你已经查了,那么用你查出来的实体来存储更新后的数据,这样id一致而且还省下资源
Context.Entry(entity).State = EntityState.Modified
这一步相当于将entity注册为修改状态,如果没有提前attach的话会自动attach。当第一次Context.SaveChanges()的时候,会将entity这条数据保存到缓存中,所以你下次如果想修改这条数据的话,需要保证两个条件:
1.第二次的entity一定是被跟踪的实体类,也就是说如果你用了AsNoTracking得到的entity,那么entity是一个新的实体类,没有被跟踪,那么无论你如何想要将其注册为Modified,都是无效的,因为它与缓存中的entity冲突。
2.不要用Attach。因为1中已经保证了是一个被跟踪的实体类,已经在缓存中,不需要再Attach。
因此,总结起来就是:
查询的时候不要用AsNoTracking
修改的时候不要用Attach