程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Entity Framework 並發處理,entityframework

Entity Framework 並發處理,entityframework

編輯:關於.NET

Entity Framework 並發處理,entityframework


參考頁面:

http://www.yuanjiaocheng.net/entity/entity-relations.html

http://www.yuanjiaocheng.net/entity/entity-lifecycle.html

http://www.yuanjiaocheng.net/entity/code-first.html

http://www.yuanjiaocheng.net/entity/mode-first.html

http://www.yuanjiaocheng.net/entity/database-first.html

什麼是並發?

並發分悲觀並發和樂觀並發。

悲觀並發:比如有兩個用戶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; }
        1484643426
        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] = @0
where (([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] = @0
where (([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即可。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved