Entity Framework (EF)

فرق Tracking و AsNoTracking در Entity Framework (EF) :

در Entity Framework (EF)، دو روش اصلی برای بازیابی داده‌ها از پایگاه داده وجود دارد: Tracking و AsNoTracking. این دو روش تاثیر مهمی بر عملکرد و رفتار پرس و جوها دارند. تفاوت‌های کلیدی بین این دو روش عبارتند از:

Tracking (ردیابی)

  • عملکرد: وقتی از ردیابی استفاده می‌کنید، Entity Framework موجودیت‌های بازیابی شده از پایگاه داده را در ChangeTracker ردیابی می‌کند. این به این معنی است که هر تغییری که در این موجودیت‌ها انجام شود، توسط EF شناسایی شده و هنگام ذخیره تغییرات (SaveChanges) به طور خودکار به پایگاه داده اعمال می‌شود.
  • مزایا:
    • قابلیت پیگیری تغییرات: اگر می‌خواهید تغییراتی که روی موجودیت‌ها انجام می‌شود را ردیابی و در نهایت به پایگاه داده اعمال کنید، ردیابی مناسب است.
    • همگام‌سازی خودکار: EF به طور خودکار تغییرات را به پایگاه داده اعمال می‌کند.
  • معایب:
    • عملکرد پایین‌تر: ردیابی موجودیت‌ها نیازمند حافظه و پردازش بیشتری است که می‌تواند در پرس و جوهای بزرگ یا پیچیده منجر به افت عملکرد شود.

AsNoTracking (بدون ردیابی)

  • عملکرد: وقتی از AsNoTracking استفاده می‌کنید، EF موجودیت‌های بازیابی شده از پایگاه داده را در ChangeTracker ردیابی نمی‌کند. این باعث می‌شود که پرس و جوها سریع‌تر اجرا شوند و حافظه کمتری مصرف کنند.
  • مزایا:
    • عملکرد بهتر: چون EF موجودیت‌ها را ردیابی نمی‌کند، پرس و جوها سریع‌تر اجرا می‌شوند و منابع کمتری مصرف می‌شود.
    • مناسب برای سناریوهای فقط خواندنی: اگر تنها به خواندن داده‌ها نیاز دارید و نمی‌خواهید آن‌ها را به روزرسانی کنید، AsNoTracking انتخاب بهتری است.
  • معایب:
    • عدم پیگیری تغییرات: تغییراتی که روی موجودیت‌ها انجام می‌شود توسط EF شناسایی نمی‌شود و باید به صورت دستی مدیریت شوند.
    • نیاز به مدیریت دستی: اگر بعداً تصمیم به اعمال تغییرات داشتید، باید موجودیت‌ها را مجدداً به ChangeTracker اضافه کنید.

مثال

در اینجا مثالی از استفاده از هر دو روش آورده شده است:

Tracking

AsNoTracking

نکات نهایی

  • استفاده از AsNoTracking برای سناریوهای فقط خواندنی (read-only) بسیار مناسب است زیرا عملکرد را بهبود می‌بخشد.
  • در صورت نیاز به ردیابی و اعمال تغییرات به پایگاه داده، باید از ردیابی استفاده کنید.
  • Entity Framework Core به طور پیش‌فرض از ردیابی Tracking استفاده می‌کند مگر اینکه به طور صریح از AsNoTracking استفاده کنید.

تفاوت بین IQueryable و IEnumerable :

در اصل متد IQueryable از متد IEnumerable ارث بری میکنه و طبیعتا تمام ویژگی های آن را دارد اما در اصل IQueryable سمت SQL ریکوئستی را ارسال نمیکند و زمانی که دستور ()ToList را بهش میدهیم میره و اطلاعات را برای ما از سمت SQL فراخوانی میکنه به گ.نه ای که میتوان بارها با دستور where  کوئری را مفصل تر نمود و در نهایت بعد از زدن ()ToList میره و اطلاعات را از SQL میاره ولی IEnumerable پس از هر بار where  زدن می بایست دستور ()ToList را نوشت و هر بار دیتا را برای ما از سمت sql میاره

توضیحات تکمیلی:

متد زیر را که یکی از اشتباهات رایج حین استفاده از LINQ خصوصا جهت Binding اطلاعات است، در نظر بگیرید:


این متد در حقیقت هیچ چیزی را Get نمی‌کند! نام اصلی آن GetQueryableCustomers و یا GetQueryObjectForCustomers است.
IQueryable قلب LINQ است و تنها بیانگر یک عبارت (expression) از رکوردهایی می‌باشد که مد نظر شما است و نه بیشتر.

برای مثال زمانیکه یک IQueryable را همانند مثال فوق فیلتر می‌کنید نیز هنوز چیزی از بانک اطلاعاتی یا منبع داده‌ای دریافت نشده است. هنوز هیچ اتفاقی رخ نداده است و هنوز رفت و برگشتی به منبع داده‌ای صورت نگرفته است.
به آن باید به شکل یک expression builder نگاه کرد و نه لیستی از اشیاء فیلتر شده‌ی ما. به این مفهوم، deferred execution (اجرای به تاخیر افتاده) نیز گفته می‌شود (باید دقت داشت که IQueryable هم یک نوع IEnumerable است به علاوه expression trees که مهم‌ترین وجه تمایز آن نیز می‌باشد.
برای مثال در عبارت زیر تنها در زمانیکه متد ToList فراخوانی می‌شود، کل عبارت LINQ ساخته شده، به عبارت SQL متناظر با آن ترجمه شده، اطلاعات از دیتابیس اخذ گردیده و حاصل به صورت یک لیست بازگشت داده می‌شود:


در مورد IEnumerable ها چطور؟

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

اگر همین دو سطر را با IQueryable بازنویسی کنیم چطور؟

در سطر اول تنها یک عبارت LINQ ساخته شده است و بس. در سطر دوم نیز به همین صورت. در طی این دو سطر حتی یک رفت و برگشت به بانک اطلاعاتی صورت نخواهد گرفت. در ادامه اگر این اطلاعات به نحوی Select شوند (یا ToList فراخوانی شود، یا در طی یک حلقه برای مثال Iteration ایی روی این حاصل صورت گیرد یا موارد مشابه دیگر)، آنگاه کوئری SQL متناظر با عبارت LINQ فوق ساخته شده و بر روی بانک اطلاعاتی اجرا خواهد شد.
بدیهی است این روش منابع کمتری را نسبت به حالتی که تمام اطلاعات ابتدا دریافت شده و سپس فیلتر می‌شوند، مصرف می‌کند (حالت بازگشت تمام اطلاعات ممکن است شامل 20000 رکورد باشد، اما حالت دوم شاید فقط 5 رکورد را بازگشت دهد).

سوال: پس IQueryable بسیار عالی است و از این پس کلا از IEnumerable ها دیگر نباید استفاده کرد؟
خیر! توصیه اکید طراحان این است که لطفا تا حد امکان متدهایی که IQueryable بازگشت می‌دهند ایجاد نکنید! IQueryable یعنی اینکه این نقطه‌ی آغازین کوئری در اختیار شما، بعد برو هر کاری که دوست داشتی با آن در طی لایه‌های مختلف انجام بده و هر زمانیکه دوست داشتی از آن یک خروجی تهیه کن. خروجی IQueryable به معنای مشخص نبودن زمان اجرای نهایی کوئری و همچنین مبهم بودن نحوه‌ی استفاده از آن است. به همین جهت متدهایی را طراحی کنید که IEnumerable بازگشت می‌دهند اما در بدنه‌ی آن‌ها به نحو صحیح و مطلوبی از IQueryable استفاده شده است. به این صورت حد و مرز یک متد کاملا مشخص می‌شود. متدی که واقعا همان فیلتر کردن محصولات را انجام می‌دهد، همان 5 رکورد را بازگشت خواهد داد؛ اما با استفاده از یک لیست یا یک IEnumerable و نه یک IQueryable که پس از فراخوانی متد نیز به هر نحو دلخواهی قابل تغییر است.

Left Join در Entity Framework با استفاده از LINQ :

برای انجام یک عملیات Left Join در Entity Framework (EF)، می‌توانید از روش‌های مختلفی استفاده کنید. یکی از رایج‌ترین روش‌ها استفاده از LINQ (Language Integrated Query) است. در ادامه، مثالی از نحوه اجرای یک Left Join در EF با استفاده از LINQ آمده است.

فرض کنید دو جدول Customers و Orders داریم و می‌خواهیم یک Left Join بین این دو جدول انجام دهیم تا تمام مشتریان و سفارش‌های آنها (در صورت وجود) را به دست آوریم.

تعریف کلاس‌های مدل





اجرای Left Join با استفاده از LINQ

در این مثال، یک Left Join بین Customers و Orders انجام می‌دهیم تا تمامی مشتریان و سفارش‌های آنها (در صورت وجود) را بازیابی کنیم.





توضیح کد

  1. تعریف کوئری:
    • از عبارت from و join برای تعریف جوین بین Customers و Orders استفاده می‌کنیم.
    • از into customerOrders برای تعریف گروه جوین استفاده می‌کنیم.
    • از from order in customerOrders.DefaultIfEmpty() برای انجام Left Join استفاده می‌کنیم. DefaultIfEmpty() اطمینان حاصل می‌کند که اگر هیچ سفارشی برای یک مشتری وجود نداشته باشد، مقدار order برابر با null خواهد بود.
  2. انتخاب داده‌ها:
    • در قسمت select، اطلاعات مشتری و سفارش (در صورت وجود) را انتخاب می‌کنیم.
    • اگر سفارشی وجود نداشته باشد، مقادیر مربوط به سفارش برابر با null خواهند بود.
  3. اجرا و نمایش نتیجه:
    • کوئری با استفاده از ToList() اجرا می‌شود.
    • نتیجه کوئری چاپ می‌شود.

این روش ساده‌ترین و رایج‌ترین روش برای انجام Left Join در Entity Framework با استفاده از LINQ است.