ویرگول
ورودثبت نام
فرشید عزیزی
فرشید عزیزیSoftware Engineer
فرشید عزیزی
فرشید عزیزی
خواندن ۶ دقیقه·۳ سال پیش

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

دوره آموزشی 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) :

  • برای value typeها کپی می‌شوند.
  • برای reference typeها هیچ کپی‌برداری صورت نمی‌گیرد و از همان نمونه به عنوان عکس فوری استفاده می‌شود.

یادآوری مهم »

تفاوت میان reference typeها و value typeها

انواع(Types) در دات نت فریم ورک یا با value type یا reference type بررسی می شوند.

  • یک value type داده ها را در حافظه تخصیصی خود نگه می دارد.
    یک value type محتویات خود را در حافظه اختصاص داده شده در stack ذخیره می کند. هنگامی که یک value type ایجاد می کنید، یک فضای واحد در حافظه برای ذخیره مقدار اختصاص داده می شود و آن متغیر مستقیماً یک مقدار را نگه می دارد. اگر آن را به متغیر دیگری اختصاص دهید، مقدار به طور مستقیم کپی می شود و هر دو متغیر به طور مستقل کار می کنند.Predefined datatypes, structures, enums از value typeها هستند
  • یک نوع مرجع حاوی اشاره گر به مکان حافظه دیگری است که داده های واقعی را نگه می دارد. متغیرهای reference type در در ناحیه دیگری از حافظه به نام heap ذخیره می شوند.از آنجا که reference type نشان دهنده آدرس متغیر به جای خود داده است، اختصاص یک متغیر reference به دیگری، داده ها را کپی نمی کند. در عوض یک کپی دوم از reference ایجاد می کند که به همان مکان stack به عنوان مقدار اصلی اشاره می کند.
از 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) دارد. دو لیست حاوی مقادیر یکسان به عنوان متفاوت تلقی می شوند.
  • قابل تغییر است(mutable type) ؛ مقادیر موجود در لیست را می توان اضافه و حذف کرد.


یادآوری»

برابری مرجع Reference equality به این معنی است که اگر دو شیء به یک آدرس در حافظه ارجاع دهند، برابر تلقی می شوند:

برابری مرجع Reference equality
برابری مرجع 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() => $&quot${Amount}&quot }

این 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

entity framework coreef coreدوره آموزشی
۳
۰
فرشید عزیزی
فرشید عزیزی
Software Engineer
شاید از این پست‌ها خوشتان بیاید