如果我在模型中使用以下字段:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[Timestamp]
public DateTime RowVersion { get; set; }
然后将该列定义为
`RowVersion` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
我从 EF 获得了正确的乐观并发行为。也就是说,我对此使用时间戳并不感到兴奋,因为它似乎只是第二个分辨率。虽然 2 个客户端尝试在 1 秒内更新同一条记录的可能性不大,但这肯定会发生,不是吗?
因此,考虑到这一点,我更喜欢一个简单的整数,它在每次更新时自动递增 1。这样就不会错过冲突。我将定义更改为:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[Timestamp]
public long RowVersion { get; set; }
问题是,MySQL 不会自动增加这个值。所以我创建了一个触发器:
CREATE TRIGGER update_row_version BEFORE UPDATE on client
FOR EACH ROW
SET NEW.RowVersion = OLD.RowVersion + 1;
现在这一切都有效了。 EF 在需要时抛出 DbUpdateConcurrencyException,并且不会因时间窗口而错过更新。但是,它使用触发器,我一直在阅读它们对性能的影响有多么糟糕。
那么有没有更好的办法呢?也许有某种方法可以覆盖 DbContext 的 SaveChanges() 以在客户端上执行 RowVersion 增量,因此只对数据库进行一次更新(我假设触发器实际上每次都会进行这两次更新)?
好吧,我想出了一个似乎不需要触发器就能很好地发挥作用的策略。
我添加了一个简单的界面:
interface ISavingChanges
{
void OnSavingChanges();
}
该模型现在看起来像这样:
public class Client : ISavingChanges
{
// other fields omitted for clarity...
[ConcurrencyCheck]
public long RowVersion { get; set; }
public void OnSavingChanges()
{
RowVersion++;
}
}
然后我像这样覆盖 SaveChanges:
public override int SaveChanges()
{
foreach (var entity in ChangeTracker.Entries().Where(e => e.State == EntityState.Modified))
{
var saveEntity = entity.Entity as ISavingChanges;
saveEntity.OnSavingChanges();
}
return base.SaveChanges();
}
这一切都按预期进行。 ConcurrencyCheck 属性是让 EF 在 UPDATE SQL 的 SET 和 WHERE 子句中包含 RowVersion 字段的关键。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)