فرشید عزیزی
فرشید عزیزی
خواندن ۶ دقیقه·۲ سال پیش

دوره آموزشی Entity FrameWork Core - قسمت هشتم

دوره آموزشی Entity FrameWork Core - قسمت هفتم

تداخل همزمانی(Concurrency conflicts) زمانی رخ می‌دهد که یک کاربر داده‌های موجودیت را به منظور اصلاح آن بازیابی می‌کند و سپس کاربر دیگری داده‌های همان موجودیت را قبل از نوشته شدن اولین تغییرات کاربر در پایگاه داده به‌روزرسانی می‌کند.

اما چطور با این موضوع مواجه شویم و با آن به عنوان یک مسئله برخورد کنیم. قبل از ادامه بحث لازم است فهم صحیحی از برخی مفاهیم که در ادامه به آنها پرداخته خواهد شد داشته باشیم.

  • همزمانی پایگاه داده (Database concurrency):
    همزمانی پایگاه داده به موقعیت هایی اطلاق می شود که در آن چندین فرآیند یا کاربر به طور همزمان به داده های مشابهی در یک پایگاه داده دسترسی دارند یا آنها را تغییر می دهند.
  • کنترل همزمانی(Concurrency control) :
    به مکانیسم های خاصی اشاره دارد که برای اطمینان از سازگاری داده ها(consistency) در حضور تغییرات همزمان استفاده می شود.

خوب EF Core کنترل همزمانی خوشبینانه(optimistic concurrency) را پیاده سازی می کند، به این معنی که به چندین فرآیند یا کاربر اجازه می دهد تا بدون نیاز به همگام سازی یا قفل کردن، تغییرات را به طور مستقل انجام دهند. در شرایط ایده آل، این تغییرات تداخلی با یکدیگر ندارند و بنابراین می توانند موفق شوند. در بدترین حالت، دو یا چند فرآیند سعی در ایجاد تغییرات متناقض خواهند داشت و تنها یکی از آنها باید موفق شود.


نحوه عملکرد کنترل همزمانی(concurrency control) در EF Core :

پراپرتی هایی که به‌عنوان concurrency tokens پیکربندی شده‌اند برای پیاده سازی کنترل خوش‌بینانه همزمانی(optimistic concurrency) استفاده می‌شوند.

هر زمان که عملیات به‌روزرسانی یا حذف در طول SaveChanges انجام شود، مقدار concurrency token در پایگاه داده با مقدار اصلی خوانده شده توسط EF Core مقایسه می‌شود.

  • اگر مقادیر مطابقت داشته باشند، عملیات می تواند کامل شود.
  • اگر مقادیر مطابقت نداشته باشند، EF Core فرض می‌کند که کاربر دیگری عملیات متناقضی را انجام داده است و تراکنش فعلی را لغو می‌کند.

وضعیتی که کاربر دیگری عملیاتی را انجام داده است که با عملیات فعلی در تضاد است، به عنوان تداخل همزمان(concurrency conflict) شناخته می شود.

ارائه دهندگان پایگاه داده(Database providers) مسئول اجرای مقایسه مقادیر توکن همزمانی هستند.

در پایگاه‌های داده رابطه‌ای EF Core مقدار concurrency token را در بند WHERE هر عبارت UPDATE یا DELETE بررسی می کند. پس از اجرای دستورات، EF Core تعداد ردیف هایی که تحت تأثیر قرار
گرفته اند را می خواند.

اگر هیچ ردیفی تحت تأثیر قرار نگیرد، تداخل همزمانی (Concurrency conflicts) شناسایی می‌شود و EF Core DbUpdateConcurrencyException را پرتاب(throws) می‌کند.


نحوه برخورد با تداخل همزمانی (concurrency conflicts) :

اگر یک کاربر سعی کند برخی از تغییرات را در یک یک موجودیت ذخیره کند، اما کاربر دیگری بروی همان موجودیت تغییراتی داده باشد، یک استثنا ایجاد می شود.

در این مرحله، برنامه به سادگی می تواند به کاربر اطلاع دهد که به دلیل تغییرات متناقض، به روز رسانی موفقیت آمیز نبوده و ادامه دهد. اما مطلوبتر این است که از کاربر خواسته شود مطمئن شود که آیا این رکورد همچنان همان اطلاعات مورد نظر او را نشان می دهد و اینکه دوباره عملیات را انجام دهد.

این فرآیند نمونه ای از نحوه برخورد با تداخل همزمانی (concurrency conflicts) است.


حل یک تضاد همزمانی شامل ادغام تغییرات معلق(pending changes) از DbContext فعلی با مقادیر موجود در پایگاه داده است. اینکه چه مقادیری با هم ادغام می شوند متفاوت است و ممکن است توسط ورودی کاربر هدایت شود.

سه مجموعه از مقادیر برای کمک به حل تعارض همزمانی (concurrency conflicts) وجود دارد:

  • مقادیر فعلی(Current values) مقادیری هستند که برنامه در تلاش بود در پایگاه داده بنویسد.
  • مقادیر اصلی(Original values) مقادیری هستند که در ابتدا از پایگاه داده، قبل از انجام هر گونه ویرایش، بازیابی شده اند.
  • مقادیر پایگاه داده(Database values) مقادیری هستند که در حال حاضر در پایگاه داده ذخیره می شوند.


رویکرد کلی برای رسیدگی به تضادهای همزمانی (concurrency conflicts) عبارت است از:

  • در طول SaveChanges استثنا، DbUpdateConcurrencyException را بگیرید.
  • از DbUpdateConcurrencyException.Entries برای تهیه مجموعه جدیدی از تغییرات برای موجودیت های تحت تأثیر استفاده کنید.
  • برای منعکس کردن مقادیر فعلی در پایگاه داده، مقادیر اصلی توکن همزمانی را بازخوانی کنید.
  • تا زمانی که هیچ تضادی رخ ندهد، فرآیند را دوباره امتحان کنید.

در مثال زیر، Person.FirstName و Person.LastName به عنوان concurrency tokens تنظیم شده اند.

using var context = new PersonContext(); // Fetch a person from database and change phone number var person = context.People.Single(p => p.PersonId == 1); person.PhoneNumber = &quot555-555-5555&quot // Change the person's name in the database to simulate a concurrency conflict context.Database.ExecuteSqlRaw( &quotUPDATE dbo.People SET FirstName = 'Jane' WHERE PersonId = 1&quot); var saved = false; while (!saved) { try { // Attempt to save changes to the database context.SaveChanges(); saved = true; } catch (DbUpdateConcurrencyException ex) { foreach (var entry in ex.Entries) { if (entry.Entity is Person) { var proposedValues = entry.CurrentValues; var databaseValues = entry.GetDatabaseValues(); foreach (var property in proposedValues.Properties) { var proposedValue = proposedValues[property]; var databaseValue = databaseValues[property]; // TODO: decide which value should be written to database // proposedValues[property] = <value to be saved>; } // Refresh original values to bypass next concurrency check entry.OriginalValues.SetValues(databaseValues); } else { throw new NotSupportedException( &quotDon't know how to handle concurrency conflicts for &quot + entry.Metadata.Name); } } } }


پراپرتی هایی که به‌عنوان concurrency tokens پیکربندی شده‌اند برای پیاده سازی کنترل خوش‌بینانه همزمانی(optimistic concurrency) استفاده می‌شوند. بیایید نحوه این پیکربندی را با هم ببینیم :

  • با استفاده از Data annotation :
public class Person { public int PersonId { get; set; } [ConcurrencyCheck] public string LastName { get; set; } public string FirstName { get; set; } }
  • با استفاده از Fluent API :
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Person>() .Property(p => p.LastName) .IsConcurrencyToken(); }


ـTimestamp/rowversion :

ـ Timestamp/rowversion یک پراپرتی است که هر بار که یک ردیف درج یا به‌روزرسانی می‌شود، یک مقدار جدید به‌طور خودکار توسط پایگاه داده تولید می‌شود. این ویژگی همچنین به عنوان یک concurrency token در نظر گرفته می‌شود و اطمینان حاصل می‌کند که اگر ردیفی که به‌روزرسانی می‌کنید از زمانی که شما آن را درخواست کرده‌اید تغییر کرده است، استثنا دریافت می‌کنید. جزئیات دقیق بستگی به ارائه دهنده پایگاه داده مورد استفاده دارد. در SQL Server معمولاً از یک []byte استفاده می شود که به عنوان یک ستون ROWVERSION در پایگاه داده تنظیم می شود.

می‌توانید یک پراپرتی را به‌صورت timestamp/rowversion پیکربندی کنید:

  • با استفاده از Data annotation :
public class Blog { public int BlogId { get; set; } public string Url { get; set; } [Timestamp] public byte[] Timestamp { get; set; } }
  • با استفاده از Fluent API :
internal class MyContext : DbContext { public DbSet<Blog> Blogs { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Blog>() .Property(p => p.Timestamp) .IsRowVersion(); } } public class Blog { public int BlogId { get; set; } public string Url { get; set; } public byte[] Timestamp { get; set; } }


بیشتر بخوانید : دوره آموزشی Entity FrameWork Core - قسمت نهم

بیشتر بخوانید : دوره آموزشی Entity FrameWork Core

بیشتر بخوانید : نقشه راه توسعه دهندگان Asp.NET Core

https://zarinp.al/farshidazizi

دوره آموزشیentity framework coreef coreasp net core
Software Engineer
شاید از این پست‌ها خوشتان بیاید