我在使用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
。然後它起作用了..但是至少從我目前的知識來看,這沒有任何意義。
我找到了這個問題,但沒有有效答案,只有關於EF如何進行跟踪的變通辦法和假設。
更新這裡是到bitbucket的鏈接,其中包含重新創建此問題的示例: EF Core更新示例
我序列化了從上下文中檢索的對象。
默認情況下,檢索實體時將對其進行跟踪,並且由於已跟踪它們,因此您可以僅調用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;
}
}