領域模型中分散的事務如何集中統一處理(C#解決方案)

開篇

什麼是事務,事務的應用場景

作項目時,常常會遇到一些需求,好比註冊用戶時,要求同時存入用戶的基本信息和初始化該用戶的賬戶,若是在這兩個環節中的任何一個地方出錯,則要求回滾全部操做,這就是事務,它的主要目的是爲了數據的完整性,即要麼全盤成功,要麼全盤失敗,你們都穿一條褲衩。數據庫

今天我要說的復瑣事務

事務的概念很簡單,應用起來也很方便,固然能夠在存儲過程當中直接實現事務保證數據的完整性,可是我通常不會這樣作,由於我基本把數據庫就當作一個數據倉庫而已,它是一個容器,沒有其餘邏輯,邏輯都放領域層來處理,由於這不是咱們今天的主要話題,因此就很少講了。回到正題,那麼咱們要在C#中實現事務是怎麼實現的呢,很簡單,直接調用Connection的BeginTransaction方法便可,咱們拿ADO.Net中的SqlServer舉例,完整的應用示例以下:框架

以上代碼兩Usr表中插入兩條數據,要求這兩條數據必須都成功插入,不然回滾,邏輯很簡單。ide

更好的方式

上面的代碼沒什麼問題,可是若是咱們每一個地方都這樣寫,那的確很繁瑣,因此出來了不少ORM框架來輔助咱們,再看一下EntityFramework是怎麼實現事務的spa

代碼:線程

using (RetailEntities context = new RetailEntities())
      {
          context.Customers.Add(entity1);
          context.Customers.Add(entity2);
          context.SaveChanges();
      }

估計你們都用過,沒錯,它在SaveChanges方法以前,自動爲咱們開始了一個事務。這樣作的好處,不言而喻,比起上面那樣作可謂是簡潔了許多。可是今天咱們不使用EntityFramework,而是使用本身的方式來解決將要面臨的問題。設計

我實現事務的方式

看過我博客的朋友有可能會知道,我之前也寫過一個ORM框架(XDBFramework),今天咱們主要不講這個框架,但會引用其中的代碼來講明問題,下面就上代碼,來看在XDBFramework中是怎樣實現事務的。3d

DataContext類code

DataAccess類,也就是上面的_da對象

 

生成的DataContext類blog

 

調用代碼:

 using (var context = new DataContext())
            {
                long count = context.Admin.Count();
                var a = DataContextStatic.Recharge.Count(s => s.State == Convert.ToInt32(1));
                string passport = "x" + DateTime.Now.Ticks;
                context.BeginTransaction();
                try
                {
                    context.Admin.Insert(new Model_Admin
                                             {
                                                 Passport = passport,
                                                 Password = "123456",
                                                 AddTime = DateTime.Now
                                             });
                    context.Admin.Insert(new Model_Admin
                                             {
                                                 Passport = passport + "_2",
                                                 Password = "123456",
                                                 AddTime = DateTime.Now
                                             });
                    context.CommitTransaction();
                }
                catch
                {
                    context.RollbackTransaction();
                }

                
            }
事務代碼

能這樣實現的關鍵在於DataContext對象擁有一個DataAccess對象,調用各個DataContext上的各個實體操做自身對應表的時候會調用同一個DataAccess對象,也就是同一個connection,同一個trasaction進行數據的檢索,插入,更新,刪除操做。這樣就實現了把多個操做放到一個事務內。

引出問題

想象一下這樣一種場景,用戶在購買一件商品時,既要生成訂單,又要扣除其賬戶的相應金額,咱們假定在生成訂單時會生成多個明細,扣除餘額時也會有多條明細。

解決辦法:

1.有一種方式就是創建一個manager類,而後把生成訂單和扣除其賬戶的相應金額的邏輯寫到一個事務放到這個manager中,若是採用這種方式,固然就不會有本文存在了。

2.這裏要以領域模型的思惟方式,即什麼對象作什麼事,以領域模型建模,這裏就有兩個模型,即用戶賬戶和訂單。

因而:  

  a.訂單實體裏有生成訂單的方法,這個方法中包含插入訂單總覽和明細。

  b.賬戶這個實體裏有扣除餘額的方法,這個方法中包含扣除餘額和扣除明細

這樣一來,就有好幾個須要事務,即:

    1,生成訂單和扣除餘額

    2,生成訂單和明細

    3,扣除餘額和扣除明細

 這樣問題就來了,它們自己是一個事務,爲了模型的完整,咱們如今將它們放在了三處。如何在最後將它們合併成一個事務呢,這就是咱們今天要解決的問題。

進入實例

場景

工做流你們應該接觸過,我如今就拿這個來講,在保存設計圖時,我須要保存那些節點和線條的位置和大小等,在這以前若是是一副被修改的流程圖我還要清除原來的元素,在這個場景中,有不少操做都要求在一個事務中,好比以前元素的清徐,新節點的保存,新鏈接線的保存,新註釋的保存等。而這裏又有不少實體,好比流程圖模版,活動節點(用戶活動,系統活動),鏈接線,註釋等,拿它們的保存方法來說都有不一樣邏輯,甚至在保存方法內也還有事務的存在。

代碼

流程圖的保存方法

能夠看到這裏開始了一個大的事務,將各元素的清除和保存都放到了這個事務中。

如下的刪除方法中又包含了事務(以"Transaction(()=>)"開始的地方)

實現

總結

主要思想是爲每一個線程分配一個DataContext對象,若是發現是同一個線程在開啓事務時,判斷是否已經開啓事務,如是則直接使用已經開啓的事務,如不然開啓一個新的事務,原理很簡單。

相關文章
相關標籤/搜索