為DbContextScope新增資料庫事務提交完成事件

風靈使發表於2018-05-28

使用EF開發應用程式的一個難點就在於對其DbContext的生命週期管理,你的管理策略是否能很好的支援上層服務 使用獨立事務,使用巢狀事務,並行執行,非同步執行等需求? Mehdi El Gueddari對此做了深入研究和優秀的工作並且寫了一篇優秀的文章,為了方便更多的童鞋學習,我已將其翻譯為中文系列 :
在EntityFramework6中管理DbContext的正確方式

當然,在使用Mehdi El Gueddari為我們提供的DbContextScope元件時,任然會遇到一些比較棘手的問題,比如:

using (var scope = _dbContextScopeFactory.Create())
{
    var db = scope.DbContexts.Get<RentalServiceDbContext>();

    var bookingInfo = db.BookingInfoes.Load(bookingId);

    //呼叫bookingInfo處理一些操作


    //當滿足一定的條件,釋出一個事件,事件的訂閱者可以訂閱該資訊並處理
    if (bookingInfo.Status == BookingStatus.Ordered)
    {
        _roomEventSvc.PublishRoomEvent(bookingInfo.RoomId, bookingInfo.Id, RoomEventType.BookingRoom);
    }

    //提交資料庫事務
    scope.SaveChanges();
}

有可能我們釋出的事件會被很快訂閱者處理,甚至在我們提交事務之前!但問題就在於事件的訂閱者很可能假定我們已經提交事務了!

當然,我們很可能想到的最簡單的處理問題的方法就是 將 釋出事件的那個程式碼片段移到 提交 事務後面。確實可以這樣,就這個Demo來說,這樣處理簡單方便。但我們可能面臨更多複雜的場景,比如這個服務方法被巢狀在另外一個服務方法內部呼叫,而且 事務也自動升級為使用外部服務的 事務,那麼這個時候上面的簡單處理方法就失效了!

最好的辦法就是我們能在 DbContextScope提交事務後,插入一段我們自己的程式碼邏輯來實現我們的業務邏輯功能,很顯然,為DbContextScope增加一個提交事務事件即可,然後就像下面這樣使用:

using (var scope = _dbContextScopeFactory.Create())
            {
                var db = scope.DbContexts.Get<RentalServiceDbContext>();

                var bookingInfo = db.BookingInfoes.Load(bookingId);

                //呼叫bookingInfo處理一些操作


                //當滿足一定的條件,釋出一個事件,事件的訂閱者可以訂閱該資訊並處理
                if (bookingInfo.Status == BookingStatus.Ordered)
                {
                    //保證我們的PublishRoomEvent方法在事務提交後執行
                    scope.DbContexts.TransactionCommitted += (sender, args) =>
                    {
                        _roomEventSvc.PublishRoomEvent(bookingInfo.RoomId, bookingInfo.Id, RoomEventType.BookingRoom);
                    };
                }

                //提交資料庫事務
                scope.SaveChanges();
            }

其實,只需要幾行程式碼就可以為DbContextScope增加事務提交事件,在這兒就不具體描述了!

相關文章