> ## Content Index
> Fetch the complete content index at: https://informatic-ar.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# مفهوم الـ Data Concurrency من خلال الـ Entity Framework
- URL: https://informatic-ar.com/entity-framework-concurrency/
- Published: 2019-03-16T09:06:53.000Z
- Updated: 2026-10-03T22:31:24.000Z
- Author: محمد أحمد حسين
- Tags: دوت نت, قواعد البيانات, #wordpress-archive, #Import 2026-10-03 20:48

في هذه المقالة سأتطرق لمفهوم الـ Data Concurrency وقد يكون فيه من الصعوبة في بداية التعامل معه بالنسبة لبعض المطورين، خصوصا أنه قد يتم تطبيقه في بعض التطبيقات خصوصا التطبيقات الحسابية أو تطبيبقات البنوك أو تطبيقات التسوق الإلكتروني.

الـ Data Concurrency تنقسم لقسمين وهما:

- Pessimistic
- Optimistic

نفترض لدينا برنامج مبيعات POS وبه جدول يُسمى Product في هذا الجدول يوجد ثلاثة حقول الاول رقم المنتج والثاني اسم المنتج والثالث سعر المنتج.

وبرنامج المبيعات هذا يتم استخدامه من قبل عدة مستخدمين .. نفترض انه يتم استخدامه من مستخدمين اثنين؛ المستخدم الأول هو البائع والمستخدم الثاني هو مدير النظام... نفترض ان البائع قام بجلب المنتج والذي رقمه 1 لبيعه.. وفي نفس اللحظة قام مدير النظام بجلب نفس رقم المنتج لتعديله او لحذفه.

فكر قليلا ما هي المشكلة هنا؟!!

دعنا نأخذ مثالا آخر.. نفترض انك تقرأ في هذه المقالة الآن وتريد إضافة تعليق، لكن قبل أن تقوم بعملة حفظ التعليق تمّ حذف المقالة من قاعدة البيانات من قبل مدير المنصة!

الآن أتوقع ان الصورة قد اكتملت في ذهنك، هذا هو مفهوم الـ Data Concurrency.

دعنا الآن نبدأ في شرح ماهي الطريقة المناسبة لوضع بعض الإجراءات التي تساعدنا على تلافي تلك المشاكل.

في هذه الحالة من تلك البرمجيات الكبيرة قد تختلف الأمور من برمجيات لأخرى.. الحلول هنا تعتمد على نوعية النظام نفسه وعلى الشئ الذي يريده صاحب النظام.. في حالة برنامج المبيعات الأول الذي أخذناه كمثال قد يقول لك صاحب المتجر ليس لدي مشكلة من ان يتم حذف المنتج او تعديله أثناء قيام البائع بجلبه في شاشة المبيعات. حسنا هنا أنت كمطور ستقوم بعمل الإجراءات المناسبة للتطبيق على تلك الحالة.

> **عندما أخذت مثال برنامج المبيعات وقلت ان صاحب المتجر يريد التعديل والحذف أثناء جلب المنتج في شاشة المبيعات، فقط أردت توضيح الحالة.. لكن في برمجيات المبيعات والبنوك تلك الآلية خاطئة ولا يتم استخدامها.. فالأصح ان لا يتم التعديل او الحذف اثناء جلب المنتج في شاشة المبيعات..حتى الإنتهاء من عملية بيعه.**  
> **هذه المقالة قد تكون مختلفة نوعا من مفهوم كيفية التعامل مع البرمجيات الضخمة وفي تقديري إذا كنت مطور مبتدئ قد ترى تلك المقالة ليست في إطار اهتمامك.. أيضا بعض المطورين لا يهتمون لموضوع الـ** Data Concurrency **لإعتبارت كثيرة منها عدم تلقيهم أي ملاحظات من العملاء الذين يعملون على أنظمتهم أو ان معظم برمجياتهم لايوجد بها هذا الشئ من الـ** Data Concurrency **لكن ما أن تدلف على برمجيات ضخمة عدد المستخدمين فيها ضخم ستواجه تلك الإشكالية ويجب أن تضعها في حسبانك وأنت تقوم بعمل برمجياتك.**  
> **او فرضنا أنك ذهبت للسوبر ماركت وأخذت قطعة شوكولاته وسعرها 10 جنيهات وذهبت لطاولة البائع (الكاشير) وقام البائع بقراءة باركود تلك الشوكولاته وظهرت على الشاشة سعرها 10 جنيهات... وقبل ان يقوم البائع بحفظ وطباعة الفاتورة لك قام مدير النظام في تلك اللحظة بتعديل سعر تلك الشوكولاته من 10 لـ 20 جنيها. وبعده قام البائع بحفظ وطباعة الفاتورة لك... عندها سيصبح إجمالي الفاتورة 20 جنيها وحينها ستبدأ أنت الزبون في الصياح في البائع.. قد تشتمه حتى وقد تقول له: لماذا في الباركود مسجلة 10 وعند بيعها 20؟ حسنا هنا يأتي دور الـ** Data Concurrency.

سأقوم ببناء تطبيق بسيط جدا يساعدنا في وضع الـ Data Concurrency في سياقها وبعد بناء التطبيق سأتناول الـ Data Concurrency في بعض النقاط وسأوضح أيضا بعض الحالات البديلة التي يمكن استخدامها.

التطبيق يتكون من جدول وحيد في قاعدة بيانات SQL Sever واسمه Products وهذه هي الحقول الخاصة بالجدول كما موضحة بالصورة أدناه.

![](https://informatic-ar.com/wp-content/uploads/2019/03/1_db.jpg) جدول الـ Product

> انتبه لحقل الـ ID قم بإعطاءه القيمة Identity وقم بجعلة مفتاح رئيسي PK

أيضا قمت بإنشاء Console App وبه فئة Class اسمها Product وبها الخصائص التالية:

```csharp
[Table("Products")]
public class Product
{
    [Key]
    public int ID { get; set; }
    [StringLength(200)]
    public string ProductName { get; set; }
    [StringLength(200)]
    public String ProductCategory { get; set; }
    public Decimal SalesPrice { get; set; }
    [Timestamp]
    public byte[] Timestamp { get; set; }
}
```

لاحظ لأخر خاصية وهي Timestamp هذه الخاصية مهمتها عمل نسخة من الحقل تساعد في حال قام المستخدم بجلب ذلك الحقل أو حذفه، وفي نفس اللحظة تقوم بعمل موازنة عندما يريد مستخدم آخر تعديل هذا الحقل. الخاصية تلك تم بلورتها بإضافة صفة Attribute واسمها Timestamp و بعض المطورين يقومون بتسمية ذلك الحقل بـ RowVersion .. وكما لاحظت اخي المطور هذا الحقل يأخذ النوع Timestamp في الـ SQL Server ويأخذ النوع Byte في الـ #C وهذا الحقل يجب ان يأخذ قيمة دائما حيث لا يتوجب ان يكون فارغ وقيمته دائما تكون Binary وهي مجموعة ارقام يقوم الـ SQL Server بتوليده فبالتالي قمنا بتعريفها كمصفوفة في فئة الـ Product.

> **لقد قمت بإستخدام** **Console App** **خصيصا لأنه يساعدني في التركيز في الشرح على موضوع المقالة فقط.. وبإمكانك أخذ ما سأكتبه هنا و إدراجه في برمجياتك سواء كانت** **ASP.NET MVC** **او** **WPF** **أو** **ASP.NET Web Form**

قم بتثبيت مكتبة الـ Entity Framework داخل مشروعك كما موضح بالصورة التالية:

![](https://informatic-ar.com/wp-content/uploads/2019/03/2_db.jpg) مكتبة الـ Entity Framework من خلال الـ Nuget

أيضا قمت بإنشاؤ فئة Class آخر بإسم ConcurrencyTestDvContext وهو الذي سيربط بين التطبيق وقاعد البيانات وهو كالتالي:

```csharp
public class concurrenyTestDbContext : DbContext
{
    public concurrenyTestDbContext() : base("name = concurrenyTestDbContext")
    {
    }
    public DbSet<Product> Products { get; set; }
}
```

أيضا قمت بتعريف ملف الإتصال من خلال ملف الـ App.config كالتالي:

```markup
  <connectionStrings>
    <add name="concurrenyTestDbContext" connectionString="Data Source=DEV01;Initial Catalog=Concurrency_DB;Integrated Security=True" providerName="System.Data.SqlClient"/>
  </connectionStrings>
```

قم بلصق الشفرة التالية في الاسلوب Main داخل الفئة Program:

لا تقم بمسح الـ **Comment** الموجود في جملة الـ **Catch** لأننا سنقوم بشرحه فيما بعد.

```csharp
static void Main(string[] args)
{
    using (var context = new concurrenyTestDbContext())
    {
        var update = context.Products.FirstOrDefault(c => c.ProductName == "Milk");
        update.ProductCategory = "Ahmed";
        try
        {
            context.SaveChanges();
        }
        catch (DbUpdateConcurrencyException ex)
        {

            // ex.Entries.Single().Reload();
            context.SaveChanges();
        }
    }
    Console.ReadLine();
}
```

قبل ان تقوم بتنفيذ هذه الشفرة قم بإدخال حقل في قاعدة البيانات وقم بإسناد الحقل كالتالي:

**ProductName = Milk**

**Price = 12**

**ProductCategory = Test**

**ليس بالضرورة كتابة نفس قيم الحقول كما كتبتها هنا بإمكانك كتابة القيم التي نود كتابتها.. فقط كتبتها هنا لكي أستطيع التوضيح لك عند تنفيذ البرنامج.**

الآن نريد تنفيذ عملية تحديث لقاعدة البيانات حيث نريد تغيير قيمة الحقل ProductCategory لـ Drinks في الحقل ProductName والذي يحمل القيمة Milk وهو ما كتبناه كالتالي:

```csharp
var update = context.Products.FirstOrDefault(c => c.ProductName == "Milk");
update.ProductCategory = "Ahmed";
```

قم بوضع **BreakPoint** في جملة الـ **()Context.SaveChanges** الـموجودة في جملة **try** وبعدها قم بالضغط على زر **F5** لتنفيذ البرنامج.

ما ان يتوقف البرنامج قم بفتح الـ **SQL Server** وقم بتعديل قيمة الحقل **ProductCategory** لأي قيمة أخرى.. بعدها قم بالرجوع للفيجوال ستديو وقم بالضغط على زر **F10** حتى نستطيع الإستمرار في تنفيذ البرنامج.

لاحظ الآن البرنامج سيقوم بعمل **Throw** وهذه الرسالة توضح لك ان هناك تعديل تم على قاعدة البيانات.. والبرنامج لن يستطيع التعديل.

![](https://informatic-ar.com/wp-content/uploads/2019/03/3_db.jpg)

وهنا تفاصيل أكثر للخطأ

![](https://informatic-ar.com/wp-content/uploads/2019/03/4_db.jpg)

**دعنا نشرح العملية بصورة أدق**

في شفرة البرنامج قمنا بإستدعاء الحقل **ProductName** والذي قيمته *Milk* وذلك حتى نقوم بتغيير تلك القيمة لـ Drinks ، هنا مجرد استدعاءنا لهذا الحقل سيقوم الـ SQL Server بإستدعاء حقل الـ **Timestamp** أيضا والذي يحمل رقم معين. لقد قمنا بعمل **BreakPoint** في جملة الـ **()Context.SaveChanges** وقمنا بتغيير قيمة الحقل لقيمة أخري، مجرد تغييرنا لقيمة الحقل سيقوم الـ SQL Server بإنشاء قيمة أخرى لحقل الـ **Timestamp** وبرجوعنا للبرنامج لعملية استمرار التنفيذ لتعديل قيمة الحقل الذذي قمنا بإستدعاءه وهو الـ **ProductCategory** سيقوم البرنامج بعملية **Throw** لأن قيمة الـ **Timestamp** هنا مختلفة وبالتالي لن يستطيع التعديل.

لو قمنا بفتح الـ **SQL Server Profiler** سنرى ان الـ **SQL Server** قام بتوليد الشفرة التالية:

```sql
exec sp_executesql N'UPDATE [dbo].[Products]
SET [ProductCategory] = @0
WHERE (([ID] = @1) AND ([Timestamp] = @2))
SELECT [Timestamp]
FROM [dbo].[Products]
WHERE @@ROWCOUNT > 0 AND [ID] = @1',N'@0 nvarchar(200),@1 int,@2 binary(8)',@0=N'Ahmed',@1=4,@2=0x00000000000007E8

```

هنا لقد قمت بتغيير الحقل بالقيمة Ahmed .. إنظر للرقم الأخير **0x00000000000007E8** هذا االرقم قام بتوليده الـ **Timestamp** فبمجرد حصول اي عملية أخرى في نفس الحقل سيقوم الـ **SQL Server** بتغيير ولن يستطيع المستخدم الأول الذي بإستدعاء الحقل والذي به الـ Timestamp القديم لن يستطيع التعديل

بإمكانك فتح الـ **SQL Server Profiler** من الـ **SQL Server Management Studio** من قائمة **Tools** كما مبين بالصورتين التاليتين.

![](https://informatic-ar.com/wp-content/uploads/2019/03/5_db.jpg)

![](https://informatic-ar.com/wp-content/uploads/2019/03/6_db.jpg)

الآن قم بحذف الـ **Comment** الموجود بجملة الـ **Catch** وقم بتعديل القم لأخرى وقم بتنفيذ البرنامج مع وضع **BreakPoint** في جملة الـ ()Context.SaveChanges وقم بتغيير الحقل في قاعدة البيانات مرة أخرى.

هنا لن يقوم البرنامج بعمل **Throw** لأنّ هذا السطر قام بعمل **Refresh** للـ **Context** وهنا تُسمى تلك العملية بـ Store Win وهي التي ستقوم بإرجاعنا لأنواع الـ Data Concurrency وتحديدا النوع **Optimistic**.

لا تنسى ان تقوم بوضع الرسائل المناسبة التي سوف تظهر للمستخدم أثناء حصول تضارب في العمل على قاعدة البيانات Conflict وحتى تصبح برمجياتك أكثر تفاعل مع المستخدمين.

في المقابل النوع الأول **Pessimistic** لا يُنصح بإستخدمه لأنه يقوم بعمل Lock للحقل الذي يستدعيه المستخدم وبالتالي إذا أراد مستخد آخر إستدعاء نفس الحقل لن يستيطع حتى يقوم المستخدم الأول الذي قام بإستدعاءه من إنهاء الإجراء الذي يريده.

هناك بعض المطورين يقوم بإستخدام الحقل Datemodefied بدلا من التعامل مع الـ Data Concurrency لكن هذه العملية مكلفة جدا لأنها تتطلب منك إستخدام مجموعة من الـ Logic للتأكد من كل عملية تعديل تتم على قاعدة البيانات.

تحياتي