更新分离数据时 EF Core Postgres DbUpdateConcurrencyException
EF Core Postgres DbUpdateConcurrencyException when updating detached data
我正在尝试在 MSTest 单元测试中更新我的 Postgres 数据库中的数据。在每次测试之前,我删除所有书籍并创建一些新书籍以确保测试的正确数据:
private static Book CreateDefaultBook(int i)
{
return new Book
{
Title = TitlePrefix + i,
Description = DescriptionPrefix + i
};
}
[TestInitialize]
public void InitializeContext()
{
using (var context = new MampfContext(DbContextOptions))
{
foreach (var contextBook in context.Books)
{
context.Entry(contextBook).State = EntityState.Deleted;
}
context.SaveChanges();
for (int i = 1; i <= NumberOfBooks; i++)
{
context.Books.Add(CreateDefaultBook(i));
}
context.SaveChanges();
}
}
这很好用。然后我尝试在测试中更新数据:
[TestMethod]
public void UpdateBookTest()
{
Book book = null;
using (var context = new MampfContext(DbContextOptions))
{
book = context.Books.FirstOrDefault(r => r.Title == TitlePrefix + 1);
Assert.IsNotNull(book);
}
book.Description = "Changed";
using (var context = new MampfContext(DbContextOptions))
{
var entry = context.Entry(book);
entry.State = EntityState.Modified;
context.SaveChanges(); //Exception!
}
using (var context = new MampfContext(DbContextOptions))
{
var updatedBook = context.Books.FirstOrDefault(r => r.Title == TitlePrefix + 1);
Assert.IsNotNull(updatedBook);
Assert.AreEqual("Changed", updatedBook.Description);
}
}
我分三步进行。首先我得到一个实体。然后我更改它,同时它与上下文分离。最后我将这本书附加到一个新的上下文并将状态设置为已修改并尝试保存更改。但是我得到一个 DbUpdateConcurrencyException 消息:
"Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions."
我正在使用 postgres 提供的乐观并发性,如他们的网站所述 https://www.npgsql.org/efcore/modeling/concurrency.html
我该如何解决这个问题?
这可能是 Npgsql 提供程序中的一个实现问题。
您检查过您的 Provider / EF Core / .NET Core 版本了吗?
https://github.com/npgsql/efcore.pg/issues/1059
如果您不明白 运行 krisztiankocsis 和 roji 的提示,您需要开始跟踪对数据库的所有查询并比较 SQL 语句和 EF Core 模型状态。
或者,也许更改数据库供应商是一种选择?
顺便说一句,这个较旧的 post 可以给你更多提示:
https://github.com/npgsql/efcore.pg/issues/19
当您调用 UseXminAsConcurrencyToken
时,这会在您的实体上设置一个 xmin
属性,它将保存 PostgreSQL 中 xmin
列的值(这是自动的-生成等)。由于您实际的 Book CLR 类型没有 xmin 成员,因此配置了影子 属性;这意味着列的值存储在上下文中而不是在 CLR 实例中。
因此,当您将加载的 CLR 实例从一个上下文移动到另一个上下文时,该值将丢失 - 因为它存储在上下文中。因此,当您尝试在新上下文中更新它时,该值默认为 0 - 这是不正确的 - 并且您会得到异常。
要解决此问题,您可以:
1. 只需在您的 Book class 上定义一个 xmin
属性 类型的 uint。这将导致使用它而不是阴影 属性,因此它会跨实例持续存在。
2. 在新上下文中重新加载实体,以重新加载值。
我正在尝试在 MSTest 单元测试中更新我的 Postgres 数据库中的数据。在每次测试之前,我删除所有书籍并创建一些新书籍以确保测试的正确数据:
private static Book CreateDefaultBook(int i)
{
return new Book
{
Title = TitlePrefix + i,
Description = DescriptionPrefix + i
};
}
[TestInitialize]
public void InitializeContext()
{
using (var context = new MampfContext(DbContextOptions))
{
foreach (var contextBook in context.Books)
{
context.Entry(contextBook).State = EntityState.Deleted;
}
context.SaveChanges();
for (int i = 1; i <= NumberOfBooks; i++)
{
context.Books.Add(CreateDefaultBook(i));
}
context.SaveChanges();
}
}
这很好用。然后我尝试在测试中更新数据:
[TestMethod]
public void UpdateBookTest()
{
Book book = null;
using (var context = new MampfContext(DbContextOptions))
{
book = context.Books.FirstOrDefault(r => r.Title == TitlePrefix + 1);
Assert.IsNotNull(book);
}
book.Description = "Changed";
using (var context = new MampfContext(DbContextOptions))
{
var entry = context.Entry(book);
entry.State = EntityState.Modified;
context.SaveChanges(); //Exception!
}
using (var context = new MampfContext(DbContextOptions))
{
var updatedBook = context.Books.FirstOrDefault(r => r.Title == TitlePrefix + 1);
Assert.IsNotNull(updatedBook);
Assert.AreEqual("Changed", updatedBook.Description);
}
}
我分三步进行。首先我得到一个实体。然后我更改它,同时它与上下文分离。最后我将这本书附加到一个新的上下文并将状态设置为已修改并尝试保存更改。但是我得到一个 DbUpdateConcurrencyException 消息:
"Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions."
我正在使用 postgres 提供的乐观并发性,如他们的网站所述 https://www.npgsql.org/efcore/modeling/concurrency.html
我该如何解决这个问题?
这可能是 Npgsql 提供程序中的一个实现问题。
您检查过您的 Provider / EF Core / .NET Core 版本了吗? https://github.com/npgsql/efcore.pg/issues/1059
如果您不明白 运行 krisztiankocsis 和 roji 的提示,您需要开始跟踪对数据库的所有查询并比较 SQL 语句和 EF Core 模型状态。
或者,也许更改数据库供应商是一种选择?
顺便说一句,这个较旧的 post 可以给你更多提示: https://github.com/npgsql/efcore.pg/issues/19
当您调用 UseXminAsConcurrencyToken
时,这会在您的实体上设置一个 xmin
属性,它将保存 PostgreSQL 中 xmin
列的值(这是自动的-生成等)。由于您实际的 Book CLR 类型没有 xmin 成员,因此配置了影子 属性;这意味着列的值存储在上下文中而不是在 CLR 实例中。
因此,当您将加载的 CLR 实例从一个上下文移动到另一个上下文时,该值将丢失 - 因为它存储在上下文中。因此,当您尝试在新上下文中更新它时,该值默认为 0 - 这是不正确的 - 并且您会得到异常。
要解决此问题,您可以:
1. 只需在您的 Book class 上定义一个 xmin
属性 类型的 uint。这将导致使用它而不是阴影 属性,因此它会跨实例持续存在。
2. 在新上下文中重新加载实体,以重新加载值。