پادشاهِ کُدنویسا شو!

مدیریت همزمانی (Concurrency Management) در Entity Framework Core

مدیریت همزمانی (Concurrency Management) در Entity Framework Core یکی از حیاتی‌ترین مباحث برای توسعه‌دهندگان دات‌نت است، به‌ویژه زمانی که با سیستم‌های مقیاس‌پذیر و پرترافیک سر و کار داریم. در این مقاله، به بررسی عمیق استراتژی‌ها، ابزارها و بهترین روش‌ها برای کنترل همزمانی در EF Core می‌پردازیم.
کینگتو - آموزش برنامه نویسی تخصصصی - دات نت - سی شارپ - بانک اطلاعاتی و امنیت

مدیریت همزمانی (Concurrency Management) در Entity Framework Core

5 بازدید 0 نظر ۱۴۰۴/۱۱/۱۴

مقدمه: چرا مدیریت همزمانی اهمیت دارد؟

در یک دنیای ایده‌آل، کاربران یکی پس از دیگری داده‌ها را ویرایش می‌کنند. اما در دنیای واقعی، موقعیتی به نام Race Condition رخ می‌دهد. فرض کنید دو کاربر (آرش و سارا) همزمان قصد دارند موجودی یک حساب بانکی که ۱۰۰ واحد است را تغییر دهند:

  1. آرش موجودی را می‌خواند (۱۰۰).

  2. سارا موجودی را می‌خواند (۱۰۰).

  3. آرش ۱۰ واحد اضافه می‌کند و ذخیره می‌کند (۱۱۰).

  4. سارا ۲۰ واحد کم می‌کند و ذخیره می‌کند (۸۰).

در اینجا تغییرات آرش کاملاً از بین می‌رود! این پدیده را Lost Update می‌نامند. برای جلوگیری از این مشکل، ما از مکانیزم‌های مدیریت همزمانی استفاده می‌کنیم.

 

انواع مدل‌های مدیریت همزمانی

به‌طور کلی دو رویکرد اصلی برای مدیریت همزمانی وجود دارد:

۱. همزمانی خوش‌بینانه (Optimistic Concurrency)

در این مدل، ما فرض می‌کنیم تداخل به‌ندرت رخ می‌دهد. بنابراین، رکوردی را قفل نمی‌کنیم. هنگام ذخیره‌سازی، بررسی می‌کنیم که آیا داده‌ها از زمانی که ما آن‌ها را خوانده‌ایم تغییر کرده‌اند یا خیر. اگر تغییر کرده باشند، یک خطا صادر می‌شود.

  • مزایا: کارایی بالا، عدم قفل شدن دیتابیس.

  • معایب: نیاز به مدیریت خطا در کد برنامه.

۲. همزمانی بدبینانه (Pessimistic Concurrency)

در این مدل، به محض اینکه کاربری داده‌ای را برای ویرایش می‌خواند، آن رکورد قفل می‌شود تا کار تمام شود.

  • مزایا: تضمین عدم تداخل.

  • معایب: کاهش شدید کارایی (Performance) و احتمال وقوع Deadlock.

 

مدیریت همزمانی خوش‌بینانه در EF Core

EF Core به‌صورت پیش‌فرض از الگوی "Last-in-Wins" استفاده می‌کند، یعنی آخرین تغییر، تغییرات قبلی را بازنویسی می‌کند. برای تغییر این رفتار، باید از Concurrency Tokens استفاده کنیم.

روش اول: استفاده از ویژگی [ConcurrencyCheck]

شما می‌توانید روی هر فیلدی که می‌خواهید حساس به همزمانی باشد، این ویژگی را قرار دهید.

public class Product
{
    public int Id { get; set; }
    
    [ConcurrencyCheck]
    public string Name { get; set; }
    
    public decimal Price { get; set; }
}

وقتی این فیلد را تغییر می‌دهید، EF Core در دستور UPDATE خود، مقدار قدیمی را در بخش WHERE چک می‌کند:

UPDATE Products SET Name = @NewName
WHERE Id = @Id AND Name = @OldName;

اگر سطر مربوطه تغییر کرده باشد، هیچ ردیفی ویرایش نمی‌شود و EF Core یک استثنا از نوع DbUpdateConcurrencyException پرتاب می‌کند.

روش دوم: استفاده از RowVersion یا Timestamp (بهترین روش)

در SQL Server، نوع داده‌ای به نام rowversion وجود دارد که با هر تغییر در سطر، دیتابیس به‌صورت خودکار مقدار آن را عوض می‌کند. این بهترین راه برای مدیریت همزمانی است.

در کلاس مدل:

public class Employee
{
    public int Id { get; set; }
    public string FullName { get; set; }
    
    [Timestamp]
    public byte[] RowVersion { get; set; }
}

با استفاده از Fluent API:

modelBuilder.Entity()
    .Property(e => e.RowVersion)
    .IsRowVersion();

 

مدیریت استثنای DbUpdateConcurrencyException

وقتی تداخلی رخ می‌دهد، شما باید تصمیم بگیرید که چه اتفاقی بیفتد. سه استراتژی رایج وجود دارد:

  1. Database Wins: تغییرات کاربر فعلی نادیده گرفته شود و داده‌های دیتابیس حفظ شود.

  2. Client Wins: تغییرات کاربر فعلی به زور روی دیتابیس اعمال شود (مشابه رفتار پیش‌فرض).

  3. Merging: مقادیر با هم مقایسه شوند و فقط فیلدهایی که تداخل ندارند ادغام شوند.

 

مثال کد برای مدیریت خطا:

try
{
    await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
    var entry = ex.Entries.Single();
    var clientValues = entry.CurrentValues;
    var databaseValues = await entry.GetDatabaseValuesAsync();

    if (databaseValues == null)
    {
        // سطر توسط کاربر دیگری حذف شده است
        throw new Exception("آیتم مورد نظر حذف شده است.");
    }

    // در اینجا می‌توانید منطق دلخواه برای ادغام را بنویسید
    // مثلاً: Client Wins
    entry.OriginalValues.SetValues(databaseValues);
    await _context.SaveChangesAsync();
}

 

همزمانی بدبینانه (Pessimistic Concurrency) در EF Core

EF Core مستقیماً API خاصی برای قفل کردن سطرها (مانند SELECT FOR UPDATE) ندارد، اما می‌توانید از تراکنش‌ها و SQL خام استفاده کنید.

استفاده از Transaction Isolation Levels

شما می‌توانید سطح انزوای تراکنش را روی RepeatableRead یا Serializable قرار دهید:

using var transaction = await _context.Database.BeginTransactionAsync(IsolationLevel.Serializable);

try 
{
    var account = await _context.Accounts.FindAsync(1);
    account.Balance -= 100;
    await _context.SaveChangesAsync();
    await transaction.CommitAsync();
}
catch 
{
    await transaction.RollbackAsync();
}

نکته: استفاده از Serializable قفل‌های سنگینی روی جداول ایجاد می‌کند و فقط در موارد بسیار حساس مالی توصیه می‌شود.

 

مقایسه روش‌ها

ویژگی

Concurrency Check

RowVersion/Timestamp

Pessimistic Locking

پیچیدگی پیاده‌سازی

متوسط

ساده

بالا

فشار بر دیتابیس

کم

بسیار کم

زیاد

دقت

محدود به فیلد خاص

کل سطر

کل تراکنش

بهترین کاربرد

سناریوهای خاص

اکثر پروژه‌های تجاری

سیستم‌های بانکی حساس

 

بهترین تمرین‌ها (Best Practices)

  1. همیشه از RowVersion استفاده کنید: اگر از SQL Server استفاده می‌کنید، استفاده از byte[] با ویژگی [Timestamp] استانداردترین روش است.

  2. مدت زمان تراکنش را کوتاه نگه دارید: هر چه تراکنش طولانی‌تر باشد، احتمال بروز بن‌بست (Deadlock) بیشتر است.

  3. به کاربر اطلاع دهید: در صورت بروز تداخل، به جای نمایش یک خطای گنگ، به کاربر بگویید که داده‌ها توسط شخص دیگری تغییر کرده و دکمه‌ای برای بازنشانی (Reload) قرار دهید.

  4. از تراکنش‌های صریح (Explicit Transactions) استفاده کنید: زمانی که چندین عملیات وابسته به هم دارید، حتماً از BeginTransaction استفاده کنید تا یکپارچگی داده‌ها حفظ شود.

 

نتیجه‌گیری

مدیریت همزمانی در EF Core انتخابی بین کارایی و دقت است. در اکثر اپلیکیشن‌های وب، Optimistic Concurrency با استفاده از RowVersion بهترین تعادل را ایجاد می‌کند. این روش اجازه می‌دهد سیستم بدون قفل‌های سنگین مقیاس‌پذیر بماند و در عین حال از فساد داده‌ها جلوگیری کند.

 

 
لینک استاندارد شده: QB7

0 نظر

    هنوز نظری برای این مقاله ثبت نشده است.
جستجوی مقاله و آموزش
دوره‌ها با تخفیفات ویژه