[英]Better way to implement a row version with EF Core and MySQL?
If I use the following field in my model: 如果我在我的模型中使用以下字段:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[Timestamp]
public DateTime RowVersion { get; set; }
and then define the column as 然后将列定义为
`RowVersion` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
I get the proper optimistic concurrency behavior from EF. 我从EF获得了正确的乐观并发行为。 That said, I'm not thrilled about using a timestamp for this since it appears to only be second resolution. 也就是说,我对使用时间戳感到很兴奋,因为它似乎只是第二个分辨率。 And while there isn't a big chance of having 2 clients try to update the same record within 1 second, it certainly could happen, no? 虽然有2个客户尝试在1秒内更新同一记录的可能性很大,但肯定会发生,不是吗?
So with that in mind I would prefer a simple integer that atomically increments by 1 on every update. 因此,考虑到这一点,我更喜欢一个简单的整数,在每次更新时以原子方式递增1。 This way there is no possibility of missing a conflict. 这样就不可能错过冲突。 I changed my definition to: 我把我的定义改为:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[Timestamp]
public long RowVersion { get; set; }
The problem is, MySQL won't automatically increment this. 问题是,MySQL不会自动增加它。 So I created a trigger: 所以我创建了一个触发器:
CREATE TRIGGER update_row_version BEFORE UPDATE on client
FOR EACH ROW
SET NEW.RowVersion = OLD.RowVersion + 1;
And now this all works. 现在这一切都有效。 EF throws the DbUpdateConcurrencyException when needed and there's no chance of missing an update due to a timing window. EF在需要时抛出DbUpdateConcurrencyException,并且由于时间窗口而不可能错过更新。 But, it uses a trigger and I keep reading about how bad they are for performance. 但是,它使用触发器,我继续阅读它们对性能有多糟糕。
So is there a better way? 那么还有更好的方法吗? Perhaps some way to override DbContext's SaveChanges() to perform the RowVersion increment on the client and therefore only have a single update on the DB (I'm assuming the trigger actually makes this two updates each time)? 也许某种方式来覆盖DbContext的SaveChanges()来在客户端上执行RowVersion增量,因此只对DB进行一次更新(我假设触发器实际上每次都进行两次更新)?
Ok, I figured out a strategy that seems to work well with no trigger needed. 好吧,我想出了一个似乎运行良好的策略,不需要触发器。
I added a simple interface: 我添加了一个简单的界面:
interface ISavingChanges
{
void OnSavingChanges();
}
The model looks like this now: 该模型现在看起来像这样:
public class Client : ISavingChanges
{
// other fields omitted for clarity...
[ConcurrencyCheck]
public long RowVersion { get; set; }
public void OnSavingChanges()
{
RowVersion++;
}
}
And then I overrode SaveChanges like this: 然后我像这样覆盖了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();
}
This is all working as expected. 这一切都按预期工作。 The ConcurrencyCheck attribute was the key to getting EF to include the RowVersion field in both the SET and WHERE clauses of the UPDATE SQL. ConcurrencyCheck属性是使EF在UPDATE SQL的SET和WHERE子句中包含RowVersion字段的关键。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.