1. 程式人生 > >EntiryFramework中事務操作例項

EntiryFramework中事務操作例項

一、在EF中使用事務操作

1.EF中的事務操作預設情況下會阻塞其他程序的讀取操作

2.分散式事務TranscationCope需引用System.data.Transcations.dll才能使用,TranscationCope進行多事務協調時需安裝與設定MSDTC元件。

3.分散式事務與EF事務二者區別,主要區別在於前者用於一個數據連線中控制資料一致性,後者在多個數據庫連線中控制資料一致性,如果不能理解,可以記憶為一個數據庫使用事務用前者,多個數據庫中使用事務用後者,如果在一個數據連線中使用TranscationCope,可以把它為理解為簡化版的事務,因為它只有Complete方法。

二、在EF中使用資料庫的事務DbContextTransaction

這種方式使用方便,但是在指定高級別鎖定時,多執行緒有的會丟擲異常,具體還不明。

該類可以繼承使用。

    //
    // 摘要:
    //     包裝對基礎儲存連線的事務物件的訪問並確保實體框架在該事務的上下文內對資料庫執行命令。通過對 System.Data.Entity.DbContextSystem.Data.Entity.Database
    //     物件呼叫 BeginTransaction() 來檢索此類的例項。
    public class DbContextTransaction : IDisposable

如果不需要特殊的事務級別控制推薦使用這種方式。

Test1 _context = new Test1();
//1.使用資料庫的事務時,多執行緒中IsolationLevel.RepeatableRead 出現回滾異常: 基礎提供程式在 Rollback 上失敗。
// System.Data.Entity.DbContextTransaction tran = _context.Database.BeginTransaction(System.Data.IsolationLevel.RepeatableRead);
System.Data.Entity.DbContextTransaction tran = _context.Database.BeginTransaction(System.Data.IsolationLevel.ReadCommitted);
using (tran)
{
    try
    {
        //1.修改省
        Area province = _context.Areas.FirstOrDefault(q => q.AreaLevel == 1);
        province.AreaName = province.AreaName + "1";
        _context.SaveChanges();
        Console.WriteLine(_context.Areas.FirstOrDefault(q => q.AreaLevel == 1).AreaName);
        //2.修改市
        Area city = _context.Areas.FirstOrDefault(q => q.AreaLevel == 2);
        city.AreaName = city.AreaName + "1";
        _context.SaveChanges();
        //丟擲異常
        throw new Exception("測試事務異常");
        tran.Commit();
    }
    catch (Exception ex)
    {
        Console.WriteLine("執行出錯:" + ex.Message);
        tran.Rollback();
    }
}

特別說明:

1.同一個事務處理中,不能出現兩個資料庫上下文例項,否則會出現死鎖

2.還有個麻煩點,就是如果丟擲異常執行回滾,EF實體快取中的資料就和資料庫出現不對應的現象,如果你的程式中上下文只有一個。解決方案參考:EntiryFramework中事務操作(三)事務回滾資料模型和資料庫不對應問題

會出現死鎖的程式碼例項:

Test1 _context = new Test1();
Test1 _context2 = new Test1();

System.Data.Entity.DbContextTransaction tran = _context.Database.BeginTransaction();
using (tran)
{
    try
    {
        //1.修改省
        Area province = _context.Areas.FirstOrDefault(q => q.AreaLevel == 1);
        province.AreaName = province.AreaName + "1";
        _context.SaveChanges();
        Console.WriteLine(_context2.Areas.FirstOrDefault(q => q.AreaLevel == 1).AreaName);
        //2.修改市
        Area city = _context2.Areas.FirstOrDefault(q => q.AreaLevel == 2);
        city.AreaName = city.AreaName + "1";
        _context2.SaveChanges();
        //丟擲異常
        throw new Exception("測試事務異常");
        tran.Commit();
    }
    catch (Exception ex)
    {
        Console.WriteLine("執行出錯:" + ex.Message);
        tran.Rollback();
    }
}

三、在EF中使用開啟連線的事務 DbTransaction

Test1 _context = new Test1();
//事務鎖定行為
//1.使用資料庫連結的事務時,RepeatableRead:在多執行緒時,會保證一次執行成功,其他在當前事務期間啟動的事務異常結束
//2.使用資料庫連結的事務時,ReadCommitted:不會阻塞當前程序內的其他執行緒的事務提交
System.Data.Common.DbConnection con = _context.Database.Connection;
if (con.State != System.Data.ConnectionState.Open)
    con.Open();
System.Data.Common.DbTransaction tran = con.BeginTransaction();
_context.Database.UseTransaction(tran);
using (tran)
{
    try
    {
        Thread.Sleep(number * 1000);
        //1.修改省
        Area province = _context.Areas.FirstOrDefault(q => q.AreaLevel == 1);
        province.AreaName = province.AreaName + "1";
        _context.SaveChanges();
        Thread.Sleep(number * 500);
        Console.WriteLine(_context.Areas.FirstOrDefault(q => q.AreaLevel == 1).AreaName);
        //2.修改市
        Area city = _context.Areas.FirstOrDefault(q => q.AreaLevel == 2);
        city.AreaName = city.AreaName + "1";
        _context.SaveChanges();
        //丟擲異常
        throw new Exception("測試事務異常");
        tran.Commit();
    }
    catch (Exception ex)
    {
        Console.WriteLine("執行出錯:" + ex.Message);
        tran.Rollback();
    }
}

特別說明:

DbContextTransaction的多個上下文問題和異常問題,在 DbTransaction也會出現

四、更多事務級別說明

1)    使用事務級別ReadUnCommited 會產生髒讀現像,意味著讀取到的為UnCommited(未提交)的資料。怎麼理解呢?在使用該隔離級別的事務開始後。更新了資料庫某一行的資料,但是事務的工作量比較大,後續還有一大堆程式碼還沒執行完呢。不巧的是有個哥們過來讀資料了,這個時候讀到的就是未提交的值,如果後繼工作一切正常,也沒什麼影響。一旦後面的程式碼執行中出錯,就會產生不一致的錯誤,適用於對事務極度自信的情況下,特點為可讀不可改。關於不可改需解釋一下,MS SQL中一條語句為最基本的執行單元了,如果一個事務中,對同條資料的更新語句未Commited的情況下,其它事務是需要等待的。

2) 使用事務級別ReadCommited 會消除髒讀現像,意味著讀取到的為Commited(已提交)的資料。在使用該隔離級別的事務開始後,除了該事務其它的查詢均會一直等待直到該事務提交。特點為不可讀不可改,

3) 使用事務級別Repeatable Read,可重複讀。它不會像ReadCommited一樣阻止Select查詢,但它會阻止Update語句。使用該隔離級別的事務開始後,會阻止其它事務更改查詢。但依然可以新增資料。

4) 使用事務級別Serializable 最嚴的一種了,與Repeatable Read相比就是在這種級別下不能新增資料。

5)  下表為簡單說明

其中增刪除改查為其它事務中對已在事務中的資料行進行增刪改查的可能行性其中刪,改,查操作多個事務中的同一條資料。√指該隔離級別下操作可未被阻塞可立即響應

隔離級別

增(其他事務)

刪(其他事務)

改(其他事務)

查(其他事務)

髒讀

讀提交

可重複讀

幻影讀

資料過期

ReadUnCommited

×

×

×

×

ReadCommited

×

×

×

×

×

×

Repeatable Read

×

×

×

Serializable

×

×

×

×

×

更多: