使用EF Core和MySQL實現行版本的更好方法是什麼?

asp.net-core c# entity-framework-core mysql

如果我在我的模型中使用以下字段:

[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增量,因此只對DB進行一次更新(我假設觸發器實際上每次都進行兩次更新)?

一般承認的答案

好吧,我想出了一個似乎運行良好的策略,不需要觸發器。

我添加了一個簡單的界面:

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字段的關鍵。



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因