Entity Framework Code First實現樂觀並發


Entity Framework Code First實現樂觀並發

不定時更新翻譯系列,此系列更新毫無時間規律,文筆菜翻譯菜求各位看官老爺們輕噴,如覺得我翻譯有問題請挪步原博客地址

本博文翻譯自:
https://www.codeproject.com/Articles/817432/Optimistic-Concurrency-in-Entity-Framework-Code-Fi

介紹

本文描述了使用Entity Framework Code First處理樂觀並發性的不同配置

並發性

在計算機科學中,並發性是系統的一個屬性,在這個系統中,多個計算同時執行,並且有可能相互影響。

在web應用程序中,這是一個多用戶環境,在保存數據庫中的數據時,可能存在並發性。並發性大致分為兩種類型:1)悲觀並發2)樂觀並發

1) 悲觀並發

數據庫中的悲觀並發包括鎖行,以防止其他用戶以影響當前用戶的方式修改數據。

在這種方法中,用戶執行一個操作,其中一個鎖被應用,其他用戶不能在該記錄上執行相同的操作,直到該鎖被釋放。

2) 樂觀並發

相比之下,在樂觀並發中,當用戶閱讀時,行不會被鎖定。當用戶試圖更新這一行時,系統必須確定該記錄是否被另一個用戶修改過,因為它被讀取了。

開始編寫代碼

讓我們創建一個控制台應用程序來探索處理樂觀並發的不同情況。

步驟

  1. 使用Visual Studio,創建控制台應用程序(文件 ->新建->項目->控制台程序(使用 Visual C# 模板)) 並將其命名為 ConcurrencyCheck.
  2. 向項目添加一個新的文件夾 Models. 然后在這個文件夾中添加兩個類文件: EducationContext.csStudent.cs .
  3. 在這個控制台應用程序中安裝EntityFramework Nuget包。在包管理器控制台中運行"_Install-Package EntityFramework"_命令來執行此操作。或者,您也可以"Nuget包管理器"理窗口安裝相同的程序。

下表顯示了用於樂觀並發的不同配置。

配置樂觀並發

Convention None
Data Annotation [Timestamp]
Fluent API .IsRowVersion()

1) Convention

Entity Framework Code First 沒有任何處理樂觀並發的約定。您可以使用Data Annotation或Fluent API來處理樂觀並發。

2) Data Annotation

Code First使用**[Timestamp] **處理樂觀並發性的屬性。

a) 修改 EducationContext.cs 文件如下:

using System.Data.Entity;

namespace ConcurrencyCheck.Models
{
    class EducationContext : DbContext
    {
        public EducationContext()
            : base("EducationContext")
        {
        }

        public DbSet<Student> Students { get; set; }
    }
}

base("EducationContext")中通過Code First 指令在 App.config 文件中使用名為"EducationContext"的連接字符串

b) 修改 Student.cs 文件如下:

using System.ComponentModel.DataAnnotations;

namespace ConcurrencyCheck.Models
{
    public class Student
    {
        public int StudentId { get; set; }

        public string RollNumber { get; set; }

        public string FirstName { get; set; }

        public string LastName { get; set; }

        [Timestamp]
        public byte[] RowVersion { get; set; }
    }
}

請注意,在 Student 類中有一個屬性 RowVersion ,它是 byte[] 類型,並被分配使用 [Timestamp] 屬性來處理樂觀的並發性。

c) 將 App.config 文件中的連接字符串更改為指向一個有效的數據庫:

<connectionStrings>
    <add name="EducationContext" providerName="System.Data.SqlClient" connectionString="Server=DUKHABANDHU-PC; Database=ConcurrencyCheck;Integrated Security=SSPI" />
</connectionStrings>

在這里,我們將數據庫名稱作為ConcurrencyCheck,它將在應用程序運行時通過 Code First 創建。

d) 修改 Program.cs 文件在每次應用程序運行時都要刪除和創建數據庫:

static void Main(string[] args)
{
    Database.SetInitializer(new DropCreateDatabaseAlways<EducationContext>());

    using (var context = new EducationContext())
    {
        context.Students.Add(new Student
        {
            FirstName = "Dukhabandhu",
            LastName = "Sahoo",
            RollNumber = "1"
        });

        context.SaveChanges();
    }

    Console.WriteLine("Database Created!!!");
    Console.ReadKey();
}

如果運行該應用程序,代碼首先將創建數據庫 ConcurrenCheck 它擁有兩個表 MigrationHistoryStudents

如果您看到 Students 表中的 RowVersion 列(在SQL Sever中),它的數據類型是 timestamp

RowVersionTimeStamp 是不同數據庫提供程序用於相同目的的兩個術語。當創建或更新 Students 表中的記錄時,數據庫將自動更新 RowVersion 值到新值。即使您為 rowversion 列發送值,數據庫(SQL Server)也不使用該值來進行插入或更新操作。

當添加到 Students 表的新記錄時生成的SQL:

exec sp_executesql N'INSERT [dbo].[Students]([RollNumber], [FirstName], [LastName])
VALUES (@0, @1, @2)
SELECT [StudentId], [RowVersion]
FROM [dbo].[Students]
WHERE @@ROWCOUNT > 0 AND [StudentId] = scope_identity()',N'@0 nvarchar(max) ,@1 nvarchar(max) ,@2 nvarchar(max) ',@0=N'1',@1=N'Dukhabandhu',@2=N'Sahoo'

您可以看到查詢不僅插入了一個新記錄,而且還返回了 RowVersion 的值。

當進行更新和刪除操作時,會發生實際的並發檢查。在更新和刪除 Students 表的記錄時,請參閱下面如何發生並發檢查。

UPDATE SQL

exec sp_executesql N'UPDATE [dbo].[Students]
SET [RollNumber] = @0 WHERE (([StudentId] = @1) AND ([RowVersion] = @2))
SELECT [RowVersion] FROM [dbo].[Students]
WHERE @@ROWCOUNT > 0 AND [StudentId] = @1',N'@0 nvarchar(max) ,@1 int,@2 binary(8)',@0=N'2',@1=1,@2=0x00000000000007D1

我們看到 WHERE 條件,在更新記錄時,它比較了 studentid (主鍵)和 RowVersion

DELETE SQL

exec sp_executesql N'DELETE [dbo].[Students]
WHERE (([StudentId] = @0) AND ([RowVersion] = @1))',N'@0 int,@1 binary(8)',@0=1,@1=0x00000000000007D1

在刪除記錄代碼之前,先創建一個查詢來比較標識符(主鍵 StudentId )和行版本( RowVersion 字段)用於樂觀並發。

3) Fluent API

Fluent API使用 IsRowVersion() 方法來配置樂觀並發。

為了測試Fluent API的配置,從 Students 類的 RowVersion 屬性中刪除 [Timestamp] 屬性,並在 EducationContext 類中覆蓋 ** onmodel()** 方法:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Student>().Property(s => s.RowVersion).IsRowVersion();
    base.OnModelCreating(modelBuilder);
}

配置非時間戳字段

如果不保留對並發檢查的專用列,您仍然可以處理並發性。有些數據庫不支持 RowVersion / Timestamp 類型的列。在這些類型的場景中,您可以使用Data Annotation或Fluent API配置來配置一個或多個用於並發檢查的字段。

配置非時間戳字段

Convention None
Data Annotation [ConcurrencyCheck]
Fluent API .IsConcurrencyToken()

1) Data Annotation

修改 Student 類,以使用 [ConcurrencyCheck] Data Annotation屬性:

public class Student
{
    public int StudentId { get; set; }

    [ConcurrencyCheck]
    public string RollNumber { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }
}

當應用程序運行時,代碼首先創建 Students 表(參見下圖)。數據庫在 RollNumber 列中不為 [ConcurrencyCheck] 屬性做任何特殊的事情。

但是當任何修改/更改發生在Students表時,代碼首先處理並發檢查。接下來閱讀代碼如何創建更新和刪除並處理並發檢查。

UPDATE SQL

exec sp_executesql N'UPDATE [dbo].[Students]
SET [RollNumber] = @0
WHERE (([StudentId] = @1) AND ([RollNumber] = @2))
',N'@0 nvarchar(max) ,@1 int,@2 nvarchar(max) ',@0=N'2',@1=1,@2=N'1'

注意 WHERE 條件。它在更新記錄時比較 StudentId (主鍵)和 RollNumber

DELETE SQL

exec sp_executesql N'DELETE [dbo].[Students]
WHERE (([StudentId] = @0) AND ([RollNumber] = @1))',N'@0 int,@1 nvarchar(max) ',@0=1,@1=N'2'

在刪除 Students 表的記錄時,它還檢查 StudentIdRollNumber 列值。如果 RollNumber 列值改變了,你現在正在更新那個記錄,然后你就會得到OptimisticConcurrencyException

2) Fluent API

使用Code First的 IsConcurrencyToken() 方法來處理非時間戳字段的並發性。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Student>().Property(s => s.RollNumber).IsConcurrencyToken();
    base.OnModelCreating(modelBuilder);
}

注意

為了測試並發效果,添加代碼來更新Students表中已有的記錄如下:

var student = context.Students.FirstOrDefault(u => u.StudentId == 1);

if (student != null)
{
    student.RollNumber = "2";
    context.SaveChanges();
}

在Visual Studio中為 context.SaveChanges() 行添加斷點。在 SaveChanges() 方法執行之前,修改數據庫中 Students 表記錄條件是StudentId = 1。

UPDATE Students SET RollNumber = '123' WHERE StudentId = 1;

現在,如果你要執行下一行語句savechanges()然后你會得到如下的一個提示:

DbUpdateConcurrencyException:

Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.

DbUpdateConcurrencyException 異常拋出,因為記錄已經修改為只讀。

總結

在本文中,我們學習了如何通過Entity Framework Code First將專用字段保存在表中,或者通過添加特殊data annotation屬性或Fluent API配置來配置處理樂觀並發性的方法。

歡迎轉載,轉載請注明翻譯原文出處(本文章),原文出處(原博客地址),然后謝謝觀看

如果覺得我的翻譯對您有幫助,請點擊推薦支持:)


免責聲明!

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



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