Entity Framework 並發處理

來源:互聯網
上載者:User

標籤:

什麼是並發?

並發分封閉式並行存取和開放式並行存取。

封閉式並行存取:比如有兩個使用者A,B,同時登入系統修改一個文檔,如果A先進入修改,則系統會把該文檔鎖住,B就沒辦法開啟了,只有等A修改完,完全退出的時候B才能進入修改。

開放式並行存取:同上面的例子,A,B兩個使用者同時登入,如果A先進入修改緊跟著B也進入了。A修改文檔的同時B也在修改。如果在A儲存之後B再儲存他的修改,此時系統檢測到資料庫中文檔記錄與B剛進入時不一致,B儲存時會拋出異常,修改失敗。

EF中如何控制並發?

Entity Framework不支援封閉式並行存取,只支援開放式並行存取。

如果要對某一個表做並發處理,就在該表中加一條Timestamp類型的欄位。注意,一張表中只能有一個Timestamp的欄位。

Data Annotations中用Timestamp來標識設定並發控制欄位,標識為Timestamp的欄位必需為byte[]類型。

public class Person    {        public int PersonId { get; set; }        public int SocialSecurityNumber { get; set; }        public string FirstName { get; set; }        public string LastName { get; set; }        1541178944        public byte[] RowVersion { get; set; }    }

Fluent API用IsRowVersion方法

modelBuilder.Entity<Person>().Property(p => p.RowVersion).IsRowVersion();

我們看到產生的資料庫中,RowVersion是timestamp類型。

下面我們寫一段代碼來測試一下:

       static void Main(string[] args)        {            var person = new Person            {                FirstName = "Rowan",                LastName = "Miller",                SocialSecurityNumber = 12345678            };            //新增一條記錄,儲存到資料庫中            using (var con = new BreakAwayContext())            {                con.People.Add(person);                con.SaveChanges();            }            var firContext = new BreakAwayContext();            //取第一條記錄,並修改一個欄位:這裡是修改了FirstName            //先不儲存            var p1 = firContext.People.FirstOrDefault();            p1.FirstName = "Steven";            //再建立一個Context,同樣取第一條記錄,修改LastName欄位並儲存            using (var secContext = new BreakAwayContext())            {                var p2 = secContext.People.FirstOrDefault();                p2.LastName = "Francis";                secContext.SaveChanges();            }            try            {                firContext.SaveChanges();                Console.WriteLine(" 儲存成功");            }            catch (DbUpdateConcurrencyException ex)            {                Console.WriteLine(ex.Entries.First().Entity.GetType().Name + " 儲存失敗");            }            Console.Read();        }

上面我們執行個體化了三個DbContext,第一個增加一條記錄到資料庫中,第二個修改剛增加的記錄但不儲存,然後第三個Context也取剛新增的記錄並儲存,最後再儲存第二個Context,結果儲存失敗。

可以看到我們的並發控製取到了作用。

分析EF產生的SQL語句:

exec sp_executesql N‘update [dbo].[People]set [LastName] = @0where (([PersonId] = @1) and ([RowVersion] = @2))select [RowVersion]from [dbo].[People]where @@ROWCOUNT > 0 and [PersonId] = @1‘,N‘@0 nvarchar(max) ,@1 int,@2 binary(8)‘,@0=N‘Francis‘,@1=1,@2=0x00000000000007D1

可以看到,它在取對應記錄的時候把RowVersion也作為篩選條件。上面例子中的secContext儲存的時候,資料庫中的RowVersion欄位的值就變了,所以firContext儲存的時候用原來的RowVersion取值,自然就取不到相應的記錄而報錯。

如果我們只是要對某個欄位作並發控制呢?別著急,EF也有辦法。

Data Annotations中用ConcurrencyCheck來標識

 public class Person    {        public int PersonId { get; set; }        [ConcurrencyCheck]        public int SocialSecurityNumber { get; set; }        public string FirstName { get; set; }        public string LastName { get; set; }        public byte[] RowVersion { get; set; }    }

Fluent API用IsConcurrencyToken方法

modelBuilder.Entity<Person>().Property(p => p.SocialSecurityNumber).IsConcurrencyToken();

上面的實體中,我們將SocialSecurityNumber(社會保險號)標識為開放式並發,也寫一個類似的代碼測試一下:

 static void Main(string[] args)        {            var person = new Person            {                FirstName = "Rowan",                LastName = "Miller",                SocialSecurityNumber = 12345678            };            //新增一條記錄,儲存到資料庫中            using (var con = new BreakAwayContext())            {                con.People.Add(person);                con.SaveChanges();            }            var firContext = new BreakAwayContext();            //取第一條記錄,並修改SocialSecurityNumber欄位            //先不儲存            var p1 = firContext.People.FirstOrDefault();            p1.SocialSecurityNumber = 123;            //再建立一個Context,同樣取第一條記錄,            //修改SocialSecurityNumber欄位並儲存            using (var secContext = new BreakAwayContext())            {                var p2 = secContext.People.FirstOrDefault();                p2.SocialSecurityNumber = 456;                secContext.SaveChanges();            }            try            {                firContext.SaveChanges();                Console.WriteLine(" 儲存成功");            }            catch (DbUpdateConcurrencyException ex)            {                Console.WriteLine(ex.Entries.First().Entity.GetType().Name + " 儲存失敗");            }            Console.Read();        }

運行結果同樣是儲存失敗,說明我們的並發控制起作用了。

分析一下EF執行的SQL:

exec sp_executesql N‘update [dbo].[People]set [SocialSecurityNumber] = @0where (([PersonId] = @1) and ([SocialSecurityNumber] = @2))‘,N‘@0 int,@1 int,@2 int‘,@0=123,@1=1,@2=12345678

可以看到,EF將我們要並發控制的列SocialSecurityNumber也作為一個篩選條件,這樣firContext儲存的時候也會因為的資料庫中SocialSecurityNumber值變了,取不到對應的記錄而更新失敗。

 補充一下:如果是EDMX如何將欄位設定為Concurrency。很簡單,在對應的欄位上右鍵-屬性。在開啟的屬性視窗中有一個併發模式,你將它選擇為Fixed即可。

 

Entity Framework 並發處理

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.