EF架構~關系表插入應該寫在事務里,但不應該是分布式事務


回到目錄

這個標題很有意思,關系表插入,就是說主表和外表鍵在插入時,可能會有同步插的情況,如在建立主表時,擴展表需要同步完成數據的初始化工作,而對於多表插入時,我們為了保證數據的一致性會針它寫在事務中,而對於.net中的事件,它在一些情況下,會不那么單純,對於ef和linq to sql來說,你的事務如果出現多次提交動作(submitchange | savechanges),那么,.net這邊會把它提升為分布式事務(MSDTC),即.net認為,對於一個數據表的操作,不會出現多個savechanges,OK,這個可以解釋的通,一個數據庫,一個提交,這是符合性能要求的,呵呵,但對於我們的架構來說,有時一疏忽,就違背了.net的這個原則,如,我們封裝的Insert方法,可能是這樣的

   public virtual void Insert(TEntity item)
        {
       
            Db.Entry<TEntity>(item);
            Db.Set<TEntity>().Add(item);
            this.SaveChanges();
           
        }

這個代碼也沒有問題,在一個插入動作完成后,系統由SaveChanges方法完成一次提交,正是由於這樣的代碼,所以我們的麻煩就來了,如果是對兩個表的操作嗎?

aRepository.Insert(a);
bRepository.Insert(b);

什么問題?數據一致性不能保證,因為沒有事務塊,呵呵,加上了再看看

  using (TransactionScope trans = new TransactionScope())
{
  aRepository.Insert(a);
  bRepository.Insert(b);
  trans.Commit();
}

OK,感覺是沒有問題了,但細一想就會看出問題來了,因為我們封裝的insert會有提交動作,所以,在這個事務塊中,.net會被認識是一個MSDTC(分布式的事務),原因是提交了多次,而如果你沒有打開msdtc服務的話,就會出現下面的黃屏了

注意:系統觸發分布式事務的前提是你的WEB服務器與數據庫服務器分別部署在兩個服務器上,一台不會出現這個問題的。

OK,這個MSDTC是怎么一回事,不是今天要說的重要,如果想學習MSDTC,請看我的這些文章:

 

第二十六回   將不確定變為確定~transactionscope何時提升為分布式事務?

第二十七回   將不確定變為確定~transactionscope何時提升為分布式事務~續

第二十八回   將不確定變為確定~transactionscope何時提升為分布式事務~再續(避免引起不必要的MSDTC)

第二十九回   將不確定變為確定~transactionscope何時提升為分布式事務~大結局

今天我們要說的是在插入關系表時,怎樣使它不被提升為分布式事務,事實上,怎么做我們已經知道了,就是方法中只出現一個savechages,呵呵,而對於主表與擴展表來說,對於自增主鍵的主表,做起來就有些麻煩了,呵呵,我們還需要借助SQL函數來實現主鍵的獲取工作,當主鍵插入后,通過SQL函數得到新值,然后再為擴展表賦值,最后一些savechange()就可以了,具體我們看一下代碼:

  int maxID = Convert.ToInt32(new TsingDa_NewLearningBarEntities()//當前表最大ID
                  .Database.SqlQuery<decimal>("SELECT IDENT_CURRENT ('Classroom_Info')")
                  .First());
            using (TransactionScope trans = new TransactionScope())
            {
                try
                {
                    db.Entry<Classroom_Info>(entity);
                    db.Set<Classroom_Info>().Add(entity);

                    //綁定學生
                    entity.User_Classroom_R.ToList().ForEach(i =>
                    {
                        i.ClassroomInfoID = maxID + 1;
                        db.Entry<User_Classroom_R>(i);
                        db.Set<User_Classroom_R>().Add(i);
                    });
                    db.SaveChanges();//是否為msdtc就看它提交的次數
                    trans.Complete();
                }
                catch (Exception)
                {
                    trans.Dispose();//出現異常,事務手動釋放
                    throw;
                }
            }

而一次savechanges所產生的SQL也是我們可以接受的,與服務器連接池中開啟一個新連接,然后把語句一條一條的發過去,雖然不是一次性發送,但結果我們也是可以接受的,呵呵。

 

補充回復:

看了xiashengwang的 回復,我自己去試了一下,果然savechange將當前上下文中所有要提交的東西包在了一個事務塊里,出現異常后,自動完成callback,所以,對 於一個數據庫來說TransactionScope可以省去,而對於多個數據庫的操作,才需要使用TransactionScope,而對於何時將 TransactionScope提升為MSDTC的級別,到目前為止,我只能說,一個上下文的savechanges不會提升,多個 savechanges就會提升到msdtc,而矛盾是,一個savechanges的話,確實不需要外加TransactionScope塊了,呵呵, 這部分文章估計我還要繼續寫了,看看有沒有辦法在同一上下文多個savechanges操作時,不讓系統提升到MSTDC的級別,呵呵。

回到目錄


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM