我知道已经提出了这样的问题,但是解决方案并没有帮助我。
[Fact]
public async Task UpdateAsync()
{
string newTitle = "newTitle1";
int newBrandId = 3;
var item = await storeContext.Items.AsNoTracking().FirstOrDefaultAsync();
item.BrandId = newBrandId;
item.Title = newTitle;
storeContext.Entry(item).State = EntityState.Detached;
await service.UpdateAsync(item); // exception inside
var updatedItem = await storeContext.Items.AsNoTracking().FirstOrDefaultAsync();
Assert.Equal(newTitle, updatedItem.Title);
Assert.Equal(newBrandId, updatedItem.BrandId);
}
public async Task UpdateAsync(T entity)
{
_dbContext.Entry(entity).State = EntityState.Modified; // exception when trying to change the state
await _dbContext.SaveChangesAsync();
}
消息:System.InvalidOperationException:无法跟踪实体类型“ Item”的实例,因为已经跟踪了另一个具有相同“ {'Id”}的键值的实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。考虑使用'DbContextOptionsBuilder.EnableSensitiveDataLogging'查看冲突的键值。
有趣的是,即使没有任何东西从数据库撤回,异常也是一样的
//var item = await storeContext.Items.AsNoTracking().FirstOrDefaultAsync();
var item = new Item()
{
Id = 1,
BrandId = newBrandId,
CategoryId = 1,
MeasurementUnitId = 1,
StoreId = 1,
Title = newTitle
};
答案 0 :(得分:8)
EF核心2.2遇到相同的问题。我从未在其他应用程序上遇到过这种情况。
最终以某种方式重写了我所有的更新功能:
public bool Update(Entity entity)
{
try
{
var entry = _context.Entries.First(e=>e.Id == entity.Id);
_context.Entry(entry).CurrentValues.SetValues(entity);
_context.SaveChanges();
return true;
}
catch (Exception e)
{
// handle correct exception
// log error
return false;
}
}
答案 1 :(得分:4)
Alexandar的答案是完全禁用跟踪,它解决了我的问题,但是我感到担忧,因为我不知道这会对我的其余应用程序产生什么影响。所以我去了Microsoft文档,发现this:
如果要操纵实体实例并使用SaveChanges()将这些更改持久保存到数据库中,则应该不禁用更改跟踪。
此方法为使用这些选项创建的所有上下文设置默认行为,但是您可以使用QueryTrackingBehavior或使用 AsNoTracking(IQueryable)和AsTracking(IQueryable)在单个查询上为上下文实例覆盖此行为。 )方法。
所以对我来说,解决方案是仅在需要时禁用跟踪。因此,我在代码的另一部分(从数据库中检索相同的条目)中使用了此代码,从而解决了我的问题:
var entry = await context
.SomeDbTable
.AsNoTracking() // this is what you're looking for
.Find(id);
答案 2 :(得分:3)
我遇到的许多问题都有一个令人讨厌的根源。 简而言之:我已经了解了为什么dbContext是作用域而不是单例的艰难方法。这是商店类型,但问题是相同的。 这是简化的测试初始化代码
public TestBase()
{
services = new ServiceCollection();
storeContext = StoreContextMock.ConfigureStoreContext(services, output);
serviceProvider = services.BuildServiceProvider();
}
public static StoreContext ConfigureStoreContext(IServiceCollection services)
{
services.AddDbContext<StoreContext>(c =>
c.UseInMemoryDatabase(Guid.NewGuid().ToString()).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));
var serviceProvider = services.BuildServiceProvider();
var storeContext = serviceProvider.GetRequiredService<StoreContext>();
storeContext .Stores.Add(new Store { Title = "John's store", Address = "NY", Description = "Electronics best deals", SellerId = "john@mail.com" });
storeContext .Stores.Add(new Store { Title = "Jennifer's store", Address = "Sydney", Description = "Fashion", SellerId = "jennifer@mail.com" });
storeContext .SaveChanges();
return storeContext ;
}
我重读了错误,最后注意到了主词
无法跟踪实体类型“商店”的实例,因为已经跟踪了另一个具有相同键值的{'Id'}实例
因此必须有一些孤立的实例来阻止我使用商店。我没有保存对s1
或s2
的任何引用,因此即使离开声明和初始化的范围,也必须storeContext
将引用存储在插入的对象上。这就是为什么我无法正常更新变量,也是为什么我从db对象中“查询”的所有导航属性都被分配了(延迟加载与此无关)。以下代码解决了我所有的问题。
public static StoreContext ConfigureStoreContext(IServiceCollection services)
{
services.AddDbContext<StoreContext>(c =>
c.UseInMemoryDatabase(Guid.NewGuid().ToString()).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));
var serviceProvider = services.BuildServiceProvider();
var storeContext = serviceProvider.GetRequiredService<StoreContext>();
var s1 = new Store { Title = "John's store", Address = "NY", Description = "Electronics best deals", SellerId = "john@mail.com" };
var s2 = new Store { Title = "Jennifer's store", Address = "Sydney", Description = "Fashion", SellerId = "jennifer@mail.com" }
storeContext .Stores.Add(s1);
storeContext .Stores.Add(s2);
storeContext .Entry<Store>(s1).State = EntityState.Detached;
storeContext .Entry<Store>(s2).State = EntityState.Detached;
storeContext .SaveChanges();
return storeContext ;
}
这是dbContext应该受作用域限制的众多原因之一。 感谢hint。
答案 3 :(得分:1)
对我来说这就是解决方案:
public void Update(int id, T obj)
{
var entry = table.Find(id);
_context.Entry(entry).CurrentValues.SetValues(obj);
}
基于 Bryan 给出的解决方案。我想我使用较新版本的 EF/Automapping。这对我有用。
答案 4 :(得分:0)
当我通过Entity Framework复制数据库中的某些记录并更改作为其他人的实体键的一列时,我遇到了同样的问题。 跟踪模式更改无法解决问题。
此问题已通过在EntityTypeConfiguration中正确设置主键来解决,以包含此处描述为x.EntityTwoKey的更改后的值。
builder.HasKey(x => new { x.EntityOneKey, x.EntityTwoKey });
答案 5 :(得分:0)
就我而言,在两个IF语句中两次运行SaveChanges时遇到此错误。我将SaveChanges移到了这两个代码块之外。只是我的服务层中的一个便条,它使用AsNoTracking();查询数据。
if (user.SendPaymentStatus)
{
user.SendPaymentStatus = false;
saveChanges = true;
//_userService.SaveChanges(user, false);
msg = GetPaymentHTML(user.MasterNodeName, user.Payee, DbMasterNode.LastPaidUtc);
Framework.Email.SendEmail(email, "MasterNode Payment - " + user.MasterNodeName, msg);
}
if (user.SendNodeStatus)
{
user.SendNodeStatus = false;
saveChanges = true;
//_userService.SaveChanges(user, false);
msg = GetStatusHTML(user.MasterNodeName, user.Payee, DbMasterNode.CurrentStatus, DbMasterNode.LastSeenUtc);
Framework.Email.SendEmail(email, "MasterNode Down - " + user.MasterNodeName, msg);
}
if (saveChanges)
{
user.SendPaymentStatus = false;
_userService.SaveChanges(user, false);
}
答案 6 :(得分:0)
尝试更新值时遇到相同的问题。然后我发现了我正在使用的问题。
services.AddDbContext<StudentContext>(option => option.UseSqlServer(Configuration.GetConnectionString("databasename")),ServiceLifetime.Singleton);
然后我删除了一生,这对我来说很好。
services.AddDbContext<StudentContext>(option => option.UseSqlServer(Configuration.GetConnectionString("databasename")));
答案 7 :(得分:0)
就我而言,在我将主键列 Id
设置为标识列后,上述问题已解决。