
دوره آموزشی Entity FrameWork Core - قسمت پانزدهم
پیش زمینه :
ردیابی تغییر(Change tracking) به این معنی است که EF Core به طور خودکار تشخیص می هد که چه تغییراتی توسط برنامه بر روی یک نمونه موجودیت بارگذاری شده انجام شده است، به طوری که می توان آن تغییرات را در هنگام فراخوانی SaveChanges در پایگاه داده ذخیره کرد. EF Core معمولاً این کار را با گرفتن یک عکس فوری(snapshot) از نمونه، زمانی که از پایگاه داده بارگذاری میشود، انجام میدهد و آن عکس فوری را با نمونهای که در اختیار برنامه قرار داده شده است مقایسه میکند.
EF Core دارای منطق داخلی برای گرفتن snapshot و مقایسه بیشتر انواع استاندارد مورد استفاده در پایگاه داده است، بنابراین کاربران معمولاً نیازی به نگرانی در مورد این موضوع ندارند.با این حال، زمانی که یک property از طریق یک value converter نگاشت میشود، EF Core نیاز به مقایسه روی user typeها دارد که ممکن است پیچیده باشد.
بهطور پیشفرض، EF Core از مقایسه برابری(equality comparison) پیشفرض تعریفشده توسط typeها استفاده میکند (مثلاً متد Equals).
ایحاد snapshot برای (reference type vs value types) :
یادآوری مهم »
تفاوت میان reference typeها و value typeها
انواع(Types) در دات نت فریم ورک یا با value type یا reference type بررسی می شوند.


از Stack برای تخصیص حافظه ثابت(static memory) و Heap برای تخصیص حافظه پویا(dynamic memory) استفاده می شود که هر دو در RAM کامپیوتر ذخیره می شوند.
متغیرهای تخصیص داده شده در Stack مستقیماً در حافظه ذخیره می شوند و دسترسی به این حافظه بسیار سریع است و هنگام کامپایل شدن برنامه به تخصیص آن پرداخته می شود. هنگامی که یک تابع یا یک متد تابع دیگری را فراخوانی می کند که به نوبه خود تابع دیگری و غیره را فراخوانی می کند، اجرای همه آن توابع تا زمانی که آخرین تابع مقدار خود را برگرداند به حالت تعلیق می ماند. Stack همیشه بصورت LIFO رزرو می شود، آخرین بلوک رزرو شده همیشه بلوک بعدی است که آزاد می شود. این امر ردیابی پشته را بسیار ساده می کند، آزاد کردن یک بلوک از پشته چیزی جز تنظیم یک اشاره گر نیست.
متغیرهای تخصیص داده شده روی heap آنها دارای حافظه در زمان اجرا هستند و دسترسی به این حافظه کمی کندتر است، اما اندازه heap فقط با اندازه حافظه مجازی محدود می شود. عنصر heap هیچ وابستگی به یکدیگر ندارند و همیشه در هر زمانی می توان به صورت تصادفی به آنها دسترسی داشت. شما می توانید یک بلوک را در هر زمان اختصاص دهید و در هر زمان آن را آزاد کنید. این امر باعث میشود ردیابی قسمتهایی از heap که در هر زمان معین اختصاص داده شده یا آزاد هستند، بسیار پیچیدهتر شود.
اگر میدانید دقیقاً چه مقدار داده را باید قبل از زمان کامپایل تخصیص دهید و خیلی بزرگ نیست، میتوانید از Stack استفاده کنید. اگر نمی دانید دقیقا به چه مقدار داده در زمان اجرا نیاز دارید یا اگر نیاز به تخصیص داده های زیادی دارید، می توانید از heap استفاده کنید.
خوب بیایید به موضوع اصلی خود بازگردیم
گفتیم زمانی که یک property از طریق یک value converter نگاشت میشود، EF Core نیاز به مقایسه روی user typeها دارد که ممکن است پیچیده باشد.برای مثال، قطعه کد زیر یک value convertor را برای پراپرتی <List<int تنظیم میکند تا value تبدیل به یک رشته JSON در پایگاه داده شود و یکvalue comparer را نیز تعریف میکند:(در قسمت قبل متوجه این شدید که اگر بخواهید یک Property از جنس List در مدل خود تعریف کنید Ef core نمیتواند نوع array را در دیتابیس ذخیره کند و برای حل این مشکل ما باید از Value Conversions استفاده می کردیم)
modelBuilder .Entity<EntityType>() .Property(e => e.MyListProperty) .HasConversion( v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null), v => JsonSerializer.Deserialize<List<int>>(v, (JsonSerializerOptions)null), new ValueComparer<List<int>>( (c1, c2) => c1.SequenceEqual(c2), c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), c => c.ToList()));
خوب برای <List<T :
یادآوری»
برابری مرجع Reference equality به این معنی است که اگر دو شیء به یک آدرس در حافظه ارجاع دهند، برابر تلقی می شوند:

توصیه می شود در صورت امکان از انواع غیرقابل تغییر (immutable types مانند classes یا structs) با value converterها استفاده کنید. این معمولاً کارآمدتر است و معنای تمیزتری نسبت به استفاده از نوع قابل تغییر(mutable type) دارد. با این حال، همانطور که گفته شد، استفاده از خصوصیات انواعی که برنامه قادر به تغییر آنها نیست معمول است. به عنوان مثال، آنچه در مورد <List<T گفته شد.
public List<int> MyListProperty { get; set; }
مثال زیر را در نظر بگیرید :
public class Post { public int Id { get; set; } public string Title { get; set; } public string Contents { get; set; } public ICollection<string> Tags { get; set; } }
خوب توجه داشته باشید <ICollection<string در اینجا هم یک نوع مرجع قابل تغییر را نشان می دهد مانند مثال بالاتر. این بدان معنی است که یک <ValueComparer<T مورد نیاز است تا EF Core بتواند تغییرات را به درستی ردیابی و تشخیص دهد.
modelBuilder.Entity<Post>() .Property(e => e.Tags) .HasConversion( v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null), v => JsonSerializer.Deserialize<List<string>>(v, (JsonSerializerOptions)null), new ValueComparer<ICollection<string>>( (c1, c2) => c1.SequenceEqual(c2), c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), c => (ICollection<string>)c.ToList()));
خوب بیایید برای درک کامل موضوع یک مثال دیگر که در قسمت قبلی نیر به آن پرداخته شده بود بیاوریم :
public readonly struct Dollars { public Dollars(decimal amount) => Amount = amount; public decimal Amount { get; } public override string ToString() => $"${Amount}" }
این value object را می توان در یک entity type استفاده کرد:
public class Order { public int Id { get; set; } public Dollars Price { get; set; } }
و هنگامی که در پایگاه داده ذخیره می شود به decimal تبدیل می شود:
modelBuilder.Entity<Order>() .Property(e => e.Price) .HasConversion( v => v.Amount, v => new Dollars(v));
خوب این value object به عنوان یک readonly struct پیاده سازی می شود. این بدان معناست که EF Core میتواند بدون مشکل از مقادیر عکسبرداری(snapshot) و مقایسه کند و دیگر نیازی به
Value Comparerها نیست.
بیشتر بخوانید : دوره آموزشی Entity FrameWork Core - قسمت هفدهم
بیشتر بخوانید : دوره آموزشی Entity FrameWork Core
بیشتر بخوانید : نقشه راه توسعه دهندگان Asp.NET Core
https://zarinp.al/farshidazizi
