در این درس می خواهیم در رابطه با بازیابی کردن و ذخیره کردن داده ها با استفاده کرد با استفاده ازEntity Framework Core صحبت کنیم. همانطور که در قسمت قبلی خدمتتان عرض شد در نوشتن یک اپلیکیشن با استفاده از Asp.Net Core استفاده کردن از تکنولوژی Entity Framework Core یک گزینه بسیار مناسب خواهد بود. تکنولوژی Entity Framework Core شبیه تکنولوژی Asp.Net Core به صورت کامل بازنویسی شده است تا ویژگی هایی از قبیل ماژولار بودن و همچنین Cross-platform بودن را دارا باشد.
بازیابی کردن و ذخیره کردن داده ها با استفاده از Entity Framework Core
به منظور بازیابی کردن داده ها با استفاده از EF Core شما می بایست پروپرتی مورد نظرتان را از کلاس DbContext انتخاب کرده و سپس با استفاده از LINQ کارهای متفاوتی از قبیل فیلتر کردن و یا انتخاب کردن پروپرتی های مختلفی از درون یک Entity را انجام بدهید. در رابطه با این موضوع در بسته ی آموزش ویدئویی LINQ در سی شارپ و بسته ی آموزش ویدئویی مباحث پیشرفته LINQ در سی شارپ صحبت کرده ایم. علاوه بر این موضوع با استفاده از دستورات LINQ شما میتوانید projection نیز انجام بدهید. منظور از projection تغییر دادن نتایج از یک تایپ به یک تایپ دیگر میباشد. مثالی که در قسمت زیر مشاهده می کنید CatalogBrand های مربوطه را از بانک اطلاعاتی بازیابی کرده و آنها را بر اساس نام مرتب کرده است. علاوه بر این با استفاده از پروپرتی Enabled مربوط به هر کدام از CatalogBrand ها آنها را فیلتر نموده و نهایتاً به یک تایپ جدید به نام SelectListItem اصطلاحا project کرده است.
یکی از ویژگیهای دستورات LINQ ، differed execution و یا اجرا شدن به تعویق افتاده می باشد که در بسته های آموزشی مختلف وب سایت پروید از آن صحبت کردهایم. این بدان معناست که در زمان تعریف کردن یک کوئری با استفاده از LINQ عملاً آن دستور اجرا نمیشود مگر اینکه از یکی از Execution Method ها استفاده کنید. منظور از Execution Method ها دستوراتی که از قبیل ToListAsync می باشند که در مثال بالا مشاهده شد. با استفاده از متود ToListAsync شما میتوانید کوئری نوشته شده توسط LINQ را فوراً اجرا کنید. اگر این کار انجام نشود دستور نوشته شده با استفاده از LINQ در واقع یک IQueryable از نوع SelectListItem را برمی گرداند و تا زمانی که در یک حلقه تکرار از قبیل foreach اقدام به پیمایش آن نکردهاید اجرا نمیشود. برگرداندن یک IQueryable به عنوان خروجی یک متد مزایا و معایب خاص خود را دارند که در بسته ی آموزش ویدئویی نکات و ترفندهای کالکشن ها و جنریک ها در سی شارپ بیشتر از آن صحبت کرده ایم. با استفاده از اینترفیس IQueryable تکنولوژی Entity Framework Core میتواند در صورت تمایل کوئری ایجاد شده را تغییر داده و دیگر قابلیت ها از قبیل فیلتر کردن و مرتب سازی را به آن اضافه کند با این وجود یکی از مشکلات استفاده کردن از این روش، رخ دادن خطاهایی در زمان اجرا شدن برنامه می باشد. این خطاها از این موضوع ریشه می گیرند که Entity Framework Core احتمالاً نمی تواند برخی از دستورات موجود در LINQ را به دستورات T-SQL ترجمه کند. بنابراین به طور کلی توصیه میشود که عملیات مورد نظر از قبیل فیلتر کردن نتایج را در متدی که دارد به داده ها دسترسی پیدا میکند انجام شود و خروجی کار در قالب یک Collection درون حافظه ای برای مثال یک لیست جنریک به عنوان نتیجه برگردانده بشود.
موضوع دیگر اینکه تکنولوژی Entity Framework Core تغییراتی که بر روی Entity های بازیابی شده از درون بانک اطلاعاتی رخ می دهند را اصطلاحاً ردگیری و یا Track می کند. این کار با استفاده از یکی از قابلیت های Entity Framework Core به نام change tracker اتفاق می افتد. به منظور ذخیره کردن تغییرات اتفاق افتاده بر روی Entity هایی که Track می شوند شما می بایست متد SaveChanges را از کلاس DbContext مورد نظر خود صدا بزنید. دقت کنید که شی DbContext مورد نظر که متد SaveChanges را از آن صدا میزنید می بایست همان شی ای باشد که با استفاده از آن Entity های مورد نظر خود را از بانک اطلاعاتی خواندهاید. اضافه کردن و حذف کردن Entity ها به طور مستقیم بر روی پروپرتی های DbSet متناظر اتفاق افتاده و دستورات متناظر با این تغییرات در بانک اطلاعاتی لحاظ میشود. مثال زیر نحوه اضافه کردن، به روز رسانی کردن و نهایتاً حذف نمودن Entity هایی را از درون بانک اطلاعاتی نشان میدهد.
تکنولوژی Entity Framework Core هم از متدهای synchronous و هم از متدهای asynchronous برای بازیابی و ذخیره کردن داده ها استفاده می کند. در وب اپلیکیشن ها توصیه می شود که از async/await و متدهای async استفاده کنید تا thread های موجود در وب سرور قفل نشده و بتوانند در کنار عملیات مربوط به data access کارهای دیگری را نیز انجام بدهند. در رابطه با این موضوع در بسته ی آموزش ویدئویی برنامه نویسی ناهمزمان (Asynchronous Programming) در دات نت صحبت کردهایم.
بازیابی کردن داده های مرتبط
همانطور که می دانید یکی از موضوعات بسیار مهم در کار کردن با داده ها ارتباط بین رکوردهای موجود در بانکهای اطلاعاتی به حساب میآید. تکنولوژی Entity Framework Core به سادگی میتواند با دادههای مرتبط با یکدیگر که اصطلاحا تحت عنوان related data نیز شناخته می شوند کار کند. زمانی که Entity Framework Core اقدام به بازیابی Entity هایی از درون بانک اطلاعاتی می کند تمامی پروپرتی های موجود را که با استفاده از رکوردهای درون بانک اطلاعاتی ذخیره شده اند بازیابی می کند. تکنولوژی Entity Framework Core به طور پیشفرض دادههای مرتبط که تحت عنوان navigation property نیز شناخته می شوند را از درون بانک اطلاعاتی بازیابی نخواهد کرد. این موضوع بدان دلیل است که Entity Framework Core تلاش میکند تا دادههایی که احتمالاً ضرورتی به وجود آنها نیست را بازیابی نکند. عدم بازیابی کردن داده های غیر ضروری می تواند در وب اپلیکیشن ها بسیار مهم باشد چرا که این نوع از نرم افزارها می بایست با سرعت بسیار بالایی request های مربوط به کاربر را پردازش کرده و به response هایی را به روشی کاملاً سریع برگردانند. اگر شما می خواهید که relationship مربوط به یک Entity با Entity های دیگر در نظر گرفته شود و دادههای مرتبط بازیابی بگردند میتوانید از تکنیک eager loading و یا بارگذاری پیشقدمانه استفاده کنید. برای انجام این کار از یک extension method به نام Include در دستور نوشته شده با استفاده از LINQ استفاده خواهید کرد. این موضوع در کد زیر نشان داده شده است:
برای باز کردن چندین relationship و یا حتی subrelationship ها از متد ThenInclude استفاده خواهیم کرد. تکنولوژی Entity Framework Core برای بازیابی کردن یک Entity و Entity های مرتبط با آن فقط از یک کوئری استفاده خواهد کرد. علاوه بر این روش شما می توانید با لحاظ کردن نام navigation property های مورد نظر خود به عنوان پارامتر ورودی متد Include فقط آن دسته از Entity های مورد نظر را بازیابی کنید. این موضوع در کد زیر نشان داده شده است:
یکی دیگر از کاربردهای دستورات LINQ استفاده کردن از متد aggregate و لحاظ نمودن یک specification می باشد. با استفاده از یک specification شما می توانید شکل داده هایی که قرار است بازیابی بشوند را نیز مشخص کرده و پروپرتی هایی که می خواهید مقدار بگیرند را لحاظ نمائید. برای مثال برنامه eShopOnWeb چندین specification دارد که عملیات eager loading را در خود کپسوله سازی کرده اند. دو مثال از این specification ها در کد زیر نشان داده شده است:
روشی دیگر برای بارگذاری کردن داده های مربوط به رکورد های مرتبط استفاده کردن از explicit loading می باشد. تکنیک explicit loading به شما اجازه می دهد تا داده های مورد نظر خود را به درون یک Entity که از قبل بازیابی شده است بارگذاری کنید. دقت کنید که در تکنیک explicit loading به ازای بارگذاری کردن هر داده مرتبط یک round trip جدید به دیتابیس ایجاد خواهد شد و این موضوع میتواند بر روی performance و یا کارایی سیستم تاثیر منفی بگذارد. از همین جهت در وب اپلیکیشن ها توصیه نمیشود که از تکنیک explicit loading استفاده کنیم.
تکنیک آخری که در حوزه بازیابی کردن داده های مرتبط وجود دارد Lazy loading و یا بارگذاری تنبلانه می باشد. تکنیک Lazy loading به صورت خودکار دادههای مرتبط را زمانی که برنامه به آنها نیاز دارد بارگذاری میکند. در تکنولوژی EF Core به طور پیش فرض در ورژن 2.1 قابلیت Lazy loading اضافه شد. البته برای استفاده کردن از Lazy loading می بایست یک NuGet package به نام Microsoft.EntityFrameworkCore.Proxies را نصب کنید. شبیه explicit loading استفاده کردن از Lazy loading در وب اپلیکیشن ها توصیه نمیشود چرا که با استفاده از این روش به ازای هر داده مرتبط می بایست یک کوئری جدید به دیتابیس ارسال شده و این موضوع طبیعتا هم بر روی performance برنامه تأثیر منفی خواهد گذاشت. متاسفانه سربار تحمیل شده توسط روش Lazy loading اغلب در زمان توسعه نرمافزار کشف نخواهد شد. این موضوع بدان دلیل است که تاخیر و یا latency در development environment کم است و Data set هایی که برنامه با آنها کار می کند بسیار کم حجم هستند. از طرفی زمانی که برنامه در production قرار می گیرد و کاربرها و request ها به مرور افزایش پیدا میکنند latency برنامه افزایش پیدا خواهد کرد و به ازای هر داده مرتبط یک request جدید به سمت دیتابیس ارسال خواهد شد که این موضوع بر روی performance برنامه تاثیر منفی خواهد گذاشت به طور کلی از روشهای Lazy loading و explicit loading در توسعه وب اپلیکیشن ها استفاده نخواهیم کرد.