我在使用EF Core 2.2.3更新.Net Core 2.2.0中的实体时遇到问题。
保存更改时发生错误。错误详情: 无法跟踪实体类型'Asset'的实例,因为已经跟踪了具有相同的{'Id'}键值的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。考虑使用
这是注册数据库上下文的方式:
services.AddDbContext(options =>
options.UseSqlServer(Configuration.GetConnectionString("DbConnection")), ServiceLifetime.Scoped);
Scoped
的生存期是默认设置的,但我写它的目的是为了更易于理解。
Anomaly
对象如下所示:
public IQueryable<Anomaly> GetAll()
{return _context.Anomalies.Include(a => a.Asset).Include(a => a.Level)
}
public async Task<Anomaly> GetAnomaly(int anomalyId, User user)
{
var anomaly = await GetAll()
.FirstOrDefaultAsync(a => a.Id == anomalyId);
return anomaly;
}
Update()
方法如下:
using (var transaction = _context.Database.BeginTransaction())
{
try
{
_context.Anomalies.Update(anomaly);
_context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
throw;
}
}
它在此事务之前包含一些检查,但在此情况下没有足够的相关性。
这是我已经跟踪到实例的错误的地方。我无法理解这种情况是怎么发生的。如果上下文为Scoped
,则
...“在这种情况下,将为每个请求创建一个新的服务实例”
如果我在PUT请求上的上下文与GET请求的上下文不同,那么如何跟踪实体?在最基本的级别上如何工作?
使其生效的唯一方法是设置从ChangeTracker
到EntityState.Detached
的所有条目的状态。然后它起作用了..但这至少没有意义,至少就我目前的知识而言。
我发现this question,但没有有效答案,只有关于EF如何进行跟踪的变通办法和假设。
更新 这是一个链接到bitbucket的链接,其中包含重新创建此问题的示例:EF Core Update Sample
我序列化了从上下文中检索到的对象。
答案 0 :(得分:1)
默认情况下,当您检索被跟踪的实体时,由于已跟踪它们,因此您可以仅调用SaveChanges而不能调用Update。您还可以使用.AsNoTracking()
检索实体而无需跟踪它们如果尚未跟踪,则需要调用Update,因此,如果您使用AsNoTracking,则确实需要在SaveChanges之前使用Update
public IQueryable<Anomaly> GetAll()
{ return _context.Anomalies
.Include(a => a.Asset)
.Include(a => a.Level);
}
public async Task<Anomaly> GetAnomaly(int anomalyId, User user)
{
var anomaly = await GetAll()
.AsNoTracking()
.FirstOrDefaultAsync(a => a.Id == anomalyId);
return anomaly;
}
您还可以检查是否已跟踪实体以知道是否调用Update:
using (var transaction = _context.Database.BeginTransaction())
{
try
{
bool tracking = _context.ChangeTracker.Entries<Anomaly>().Any(x => x.Entity.Id == anomaly.Id);
if (!tracking)
{
_context.Anomalies.Update(anomaly);
}
_context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
throw;
}
}
答案 1 :(得分:1)
我试图更新相同数据而没有注意到。我花了十个小时才意识到这一点。如果您有像我这样的重复值,我建议删除该数据...阅读此答案的人可能像我一样尝试了互联网上的所有解决方案,破坏了项目,遇到了相同的错误,并且忽略了重复< / strong>数据,就像我一样。你不孤单,我的朋友。
model.GroupBy(gb => gb.ID).Select(s=>s.First()).ToList();//remove duplicates!!!!!
答案 2 :(得分:0)
因此,最后,我们最终使用了自定义的UpdateEntity方法将所做的更改保存在某些实体上。此方法遍历实体的每个属性,导航属性和集合属性,确保不存在链,并一次更新对象。
它适用于大型物体,我们仅在这种情况下使用。对于简单的操作,我们继续使用简单的更新
Here is the link to a bitbucket repository with the source code
要使用此方法,必须首先获取db实体。然后,使用数据库实体和请求收到的您的实体调用UpdateEntity方法。
我希望它能对您有所帮助。干杯!
答案 3 :(得分:0)
我遇到了同样的问题,但是试图向同一Entiy添加第二行。就我而言,这是因为我假设主键是一个Int Identity并且是自动生成的。 由于我没有为其分配任何值,因此第二行具有相同的ID,这是默认值。 经验教训,当您在EF中执行Add()操作时看到此错误时,请确保您的密钥是IDENTITY。
答案 4 :(得分:0)
在我的例子中的问题来自这样一个事实,即更新 1:many 关系中的列表时缺少类定义中的实体。
public class Exercise
{
public Guid Id { get; set; }
//other props...
public List<EntityInExercise> Entities { get; set; } = new();
}
public class EntityInExercise
{
public Guid Id { get; set; }
//other props...
//?this line was missing ?
public Exercise Exercise{ get; set; }= null!;
//☝️
}
在这两种情况下(有和没有丢失的行)它都会创建相同的数据库。我遇到的唯一问题是尝试使用 Automapper 及其 Collections 更新该列表时。