拨打电话时SaveChanges/ SaveChangesAsync在实体框架(CF,C#),如果发生冲突的改变(例如,值一直上次读取啄更新),那么这两个异常DbUpdateConcurrencyException或OptimisticConcurrencyException应我赶上?
它们之间有什么区别?
c# entity-framework optimistic-concurrency entity-framework-6
我想知道在无法在请求之间保留具有特定版本的实体实例的系统中,实现乐观锁定(乐观并发控制)的最佳方法是什么。这实际上是一个非常常见的场景,但是几乎所有示例都基于在请求之间(在http会话中)保存已加载实体的应用程序。
如何在尽可能少的API污染的情况下实现乐观锁定?
栈是带有JPA(休眠)的Spring,如果这有任何关系的话。
@Version仅使用时出现问题在许多文档中,您似乎所需要做的就是用装饰一个字段,@Version并且JPA / Hibernate将自动检查版本。但是,只有将加载的对象及其当前版本保存在内存中,直到更新更改了同一实例,这才起作用。
@Version在无状态应用程序中使用时会发生什么:
id = 1并获取Item(id = 1, version = 1, name = "a")id = 1并获取Item(id = 1, version = 1, name = "a")Item(id = 1, version = 1, name = "b")EntityManager返回的项目Item(id = 1, version = 1, name = "a"),它更改name和持久化Item(id = 1, version = 1, name = "b")。Hibernate将版本增加到 …java hibernate jpa optimistic-locking optimistic-concurrency
当您希望代码在竞争条件下工作时,通常开发人员使用乐观并发控制(OCC).来自维基百科:
...在提交之前,每个事务都会验证没有其他事务修改了它已读取的数据.如果检查显示有冲突的修改,则提交事务将回退...
实现OCC的方法是检查version要修改的数据.如果版本不同,那么其他事务已修改数据,并由应用程序决定如何解决冲突(重新尝试,通知用户......).
草案如下:
class Repository
{
public class save($data)
{
$currentVersion = $data->version;
$data->version = $currentVersion + 1;
$result = $this->db->update($data, [
'id' => $data->id,
'version' => $currentVersion
]);
if (1 === $result) {
// everything ok
} else {
// conflict!
}
}
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,因为EventSourcing我们只追加域中发生的所有事件,我们不能再使用这种方法来实现OCC.在使用EventSourcing时,哪些其他方法可以保留OOC?
一个可行的选项,它可以在存储时查找冲突事件.这种方法允许对事件进行细粒度控制.我不知道这是否会使解决方案复杂化,或者我认为这是一个"标准",我在http://danielwhittaker.me/2014/09/29/handling-concurrency-issues-cqrs-event-sourced指出-系统/
问题描述中的任何空白都是值得赞赏的.提前致谢!
我试图想出一些好的词来解释用户的乐观并发异常.事实证明,我认为它会更难.我到目前为止最好的是:
其他人已经修改了你正在处理的记录.他们的新价值如下所示.请重新制作您所做的更改.
这对我来说有点糟糕,他们必须做得更好.有什么想法吗?
这里和这里已经多次询问过,但是在我的情况下没有一个答案是合适的,因为我不想在PL/PgSQL函数中执行我的更新语句并使用GET DIAGNOSTICS integer_var = ROW_COUNT.
我必须在原始SQL中执行此操作.
例如,在MS SQL SERVER中,我们有@@ ROWCOUNT,可以使用如下所示:
UPDATE <target_table>
SET Proprerty0 = Value0
WHERE <predicate>;
SELECT <computed_value_columns>
FROM <target>
WHERE @@ROWCOUNT > 0;
Run Code Online (Sandbox Code Playgroud)
在数据库的一次往返中,我知道更新是否成功并返回计算值.
什么可以用来代替'@@ ROWCOUNT'?有人可以确认此时这实际上是不可能的吗?
提前致谢.
编辑1:我确认我需要使用原始SQL(我在原始描述中写了"raw plpgsql").
为了使我的问题更清楚,请考虑更新语句只影响一行并考虑乐观并发:
客户首先做了一份SELECT声明.
他构建了UPDATE并知道哪些数据库计算列将包含在SELECT子句中.除其他外,谓词包括每次更新行时计算的时间戳.
所以,如果我们返回1行,那么一切都OK.如果没有返回行,那么我们知道先前有更新,并且客户端可能需要在再次尝试更新子句之前刷新数据.这就是为什么我们需要知道在返回计算列之前受update语句影响的行数.如果更新失败,则不应返回任何行.
我想在原子操作中进行check-then-update.我正在使用dbcontext来管理事务.如果记录被另一个线程修改但是没有抛出异常,我期望得到一个异常.任何帮助,将不胜感激.这是我的输出:
Thread-4: Reading...
Thread-5: Reading...
Thread-5: Updating destination 1
Thread-4: Updating destination 1
Thread-4: SaveChanges
Thread-5: SaveChanges
Run Code Online (Sandbox Code Playgroud)
这是我的代码片段:
public static void Main(string[] args)
{
PopulateData();
(new Thread(UpdateDestination1)).Start();
(new Thread(UpdateDestination1)).Start();
}
public static void UpdateDestination1()
{
using (var context1 = new BreakAwayContext())
{
Console.WriteLine("Thread-{0}: Reading...", Thread.CurrentThread.ManagedThreadId);
var d = context1.Destinations.FirstOrDefault();
Console.WriteLine("Thread-{0}: Updating destination {1}", Thread.CurrentThread.ManagedThreadId, d.DestinationId);
d.Name = "Thread-" + Thread.CurrentThread.ManagedThreadId;
try
{
context1.SaveChanges();
Console.WriteLine("Thread-{0}: SaveChanges", Thread.CurrentThread.ManagedThreadId);
}
catch (OptimisticConcurrencyException)
{
Console.WriteLine("OptimisticConcurrencyException!!!");
throw;
}
}
}
Run Code Online (Sandbox Code Playgroud) 当我的应用程序出现乐观并发问题时,我的应用程序中抛出StaleObjectStateException而不是OptimisticLockException(因为我读到我应该期待这个).无需发布代码,因为它是最基本的并发问题 - 时间戳列中的错误版本.
我怎么会得到OptimisticLockException,而不是另一个?
我有一个拥有多对多自我关系的实体.作为一个例子考虑这个实体:
public class User
{
public int ID { get; set; }
public string UserName { get; set; }
public virtual ICollection<User> Friends { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
以下是我配置映射的方法:
HasMany(t => t.Friends).WithMany()
.Map(m => {
m.MapLeftKey("UserID");
m.MapRightKey("FriendID");
m.ToTable("UserFriends");
});
Run Code Online (Sandbox Code Playgroud)
由于此关系现在由EF管理,我实际上无法访问UserFriends DbSet我的代码并且无法处理对它的并发访问.为了使这个组合处理并发访问(添加/删除),我是否需要自己处理多对多关系,然后添加一个[Timestamp]列或者有没有办法告诉EF自己同时处理它?就像模型构建器中的配置一样.
编辑:我正在使用EF 6,目前如果实体上有并发操作(例如,尝试删除当前未在数据库上退出的朋友),我收到以下错误消息,并且DbUpdateException:
保存不公开其关系的外键属性的实体时发生错误.EntityEntries属性将返回null,因为无法将单个实体标识为异常的来源.通过在实体类型中公开外键属性,可以更轻松地在保存时处理异常.有关详细信息,请参阅InnerException.
假设一个具有多个并发生成器的系统,每个生成器都努力使用以下可通过其名称唯一标识的公共实体来持久保存一些对象图:
CREATE TABLE CommonEntityGroup(
Id INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
Name NVARCHAR(100) NOT NULL
);
GO
CREATE UNIQUE INDEX IX_CommonEntityGroup_Name
ON CommonEntityGroup(Name)
GO
CREATE TABLE CommonEntity(
Id INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
Name NVARCHAR(100) NOT NULL,
CommonEntityGroupId INT NOT NULL,
CONSTRAINT FK_CommonEntity_CommonEntityGroup FOREIGN KEY(CommonEntityGroupId)
REFERENCES CommonEntityGroup(Id)
);
GO
CREATE UNIQUE INDEX IX_CommonEntity_CommonEntityGroupId_Name
ON CommonEntity(CommonEntityGroupId, Name)
GO
Run Code Online (Sandbox Code Playgroud)
例如,生产者A保存了一些CommonEntityMeetings,而生产者B保存了CommonEntitySets.他们中的任何一个都必须坚持CommonEntity与他们的特定物品相关.
基本上,关键点是:
c# sql-server entity-framework optimistic-concurrency sql-insert
我在 PostgreSQL 中使用 Entity Framework 6。我有一个实体,我想在其中防止并发问题,按照本文档,我添加了一个带有 [Timestamp] 属性的 RowVersion 属性,但是在保存对实体的更改后,列 RowVersion 值在数据库中保持不变。
[Timestamp]
public byte[] RowVersion { get; set; }
Run Code Online (Sandbox Code Playgroud)
我是不是遗漏了什么,还是有其他方法可以在 PostgreSQL 中处理它?
postgresql entity-framework rowversion optimistic-concurrency
c# ×4
hibernate ×2
postgresql ×2
concurrency ×1
exception ×1
java ×1
jpa ×1
plpgsql ×1
rowversion ×1
sql-insert ×1
sql-server ×1