آموزش Entity Framework
بسم الله الرحمن الرحیم
آموزش Entity Framework
بهینه سازی Entity Framework
قبل از مطالعه این دوره آموزشی بهتر است دوره های زیر را نیز مطالعه کنید.
آموزش Code first
آموزش migration و Entity Framework در Code First
بخش اول
در ادامه سری مقالات مربوط به بهینه سازی SQL و بهینه سازی کوئری در این دوره قصد داریم در خصوص افزایش کارایی در Entity Framework صحبت کنیم. برای مطالعه مقالات بهینه سازی می توانید از طریق لینک های زیر اقدام فرمایید.
بهینه سازی کوئری
بهینه سازی SQL
در تمامی برنامه ها اکثر برنامه نویسان برای واکشی و بروزرسانی اطلاعات درون پایگاه داده خود از دستوراتی استفاده می کنند که مستقیما بر روی پایگاه داده اجرا می شود (مانند FirstOrDefault و یا ToList()) این روش، روش اشتباهی نیست اما طبیعتا هزینه ای برای ارسال اطلاعات به سمت سرور و دریافت نتیجه اجرای این دستور به برنامه شما تحمیل خواهد شد.
یکی از روش هایی که می توانید این مشکل را بر طرف نمایید استفاده از دستور Local می باشد. این دستور به جای فعالیت بر روی پایگاه داده بر روی Context شما کار خواهد کرد و هیچگاه دستوری را برای SQL Server ارسال نخواهد کرد و همین امر باعث افزایش سرعت اجرای دستورات شما خواهد شد.
برای آموزش نحوه کار کردن با این دستور، یک مثال را انجام خواهیم داد.
یک پروژه MVC ایجاد کنید و Model ایی با نام Ably به آن اضافه کنید. این مدل می بایست به صورت زیر باشد:
public class Ably
{
public int Id { get; set; }
public string PostName { get; set; }
}
سپس Controller خود را ایجاد می کنیم و در آن کد زیر را برای load اطلاعات می نویسیم.
#region Example Entity Framework Load
// Load all Ablies from the database into the context
context.Ablies.Load();
// Add a new blog to the context
context.Ablies.Add(new Ably { PostName = "The ASP.NET MVC" });
// Mark one of the existing Ablies as Deleted
context.Ablies.Remove(context.Ablies.Find(1));
// Loop over the Ablies in the context.
Response.Write("In Local: <br />");
foreach (var ably in context.Ablies.Local)
{
Response.Write(string.Format(
"Found {0}: {1} with state {2}< br/>",
ably.Id,
ably.PostName,
context.Entry(ably).State));
}
// Perform a query against the database.
Response.Write("\nIn DbSet query: ");
foreach (var ably in context.Ablies)
{
Response.Write(string.Format(
"Found {0}: {1} with state {2}< br/>",
ably.Id,
ably.PostName,
context.Entry(ably).State));
}
#endregion
اگر دقت کنید کدهای بالا بسیار ساده هستند ما ابتدا تمامی رکورد های Model خود را Load کردیم. سپس یک رکورد جدید به Model خود اضافه کرده ایم و در خط بعد یک رکورد را جستجو و حذف کرده ایم و در نهایت با استفاده از دو حلقه foreach اطلاعات را چاپ کرده ایم.
حلقه اول رکوردهای مربوط به context را نمایش می دهد، این رکوردها رکورد هایی هستند که در ابتدای برنامه ما آنها را Load کردیم و سپس به این رکوردها یک رکورد اضافه کردیم و همچنین یک رکورد از این رکورد ها را حذف کرده ایم (این حلقه اطلاعات context را چاپ می کند و با پایگاه داده ما ارتباطی ندارد، هر چند context ما در ابتدای برنامه از روی پایگاه داده پر شده است).
حلقه دوم رکوردهای موجود در پایگاه داده را چاپ می کند.
اگر ما قبلا در Table خود دو رکورد به صورت " The Code First POST " با ID یک (1) و " The Entity Framework POST" با ID دو (2) داشته باشیم. خروجی برنامه ما به صورت زیر است.
In Local:
Found 0: The ASP.NET MVC POST with state Added
Found 2: The Entity Framework POST with state Unchanged
In DbSet query:
Found 1: The Code First POST with state Deleted
Found 2: The Entity Framework POST with state Unchanged
همانطور که مشاهده می کنید با استفاده از دستور context.Entry(ably).State ما وضعیت هر یک از رکورد ها را در اینجا نمایش داده ایم. این دستور در واقع مقدار رکورد را درون پایگاه داده بررسی می کند بر اساس وضعیت موجود یکی از 5 مقدار زیر را می تواند داشته باشد.
Added
Deleted
Detached
Modified
Unchanged
در خروجی مثال بالا همانطور که مشاهده می کنید رکورد جدید اضافه شده “The ASP.NET MVC POST” دارای وضعیت Added است و Id این رکورد برابر 0 است زیرا این رکورد هنوز در پایگاه داده اضافه نشده است و Id ایی نیز به انتساب داده نشده است.
رکورد " The Code First POST " نیز دارای وضعیت Deleted است و همانطور که می بینید در بخش نمایش اطلاعات Local این رکورد وجود ندارد زیرا این رکورد توسط ما حذف شده اما چون هنوز پایگاه داده ما بر روز نشده است در بخش نمایش اطلاعات DbSet این رکورد با وضعیت Deleted نمایش داده شده است.
در نهایت رکورد “The ASP.NET MVC POST” نیز به در بخش DbSet مشاهده نمی شود زیرا این رکورد نیز به پایگاه داده هنوز اضافه نشده است.
Entity Framework
چند نکته
برای استفاده از این دستور ترجیحا آخرین نسخه Entity Framework را نصب کنید
این دستور تا زمانی که Model شما به Context اضافه نشده است قابل دسترسی نیست، پس حتما باید برنامه Build شود. در صورتی که این دستور را مشاهده نکردید update-database را اجرا کنید.
دستور Local
با استفاده از این دستور شما می توانید به جای کار بر روی اطلاعات پایگاه داده، ابتدا اطلاعات مورد نظر خود را درون Context بارگذاری (load) کنید و سپس بر روی این اطلاعات دستورات خود را اجرا و در نهایت این تغییرات به پایگاه داده ارسال کنید.
این دستور به همراه دستور Load برای انجام عملیات ها و کاهش هزینه بر روی دیتابیس مورد استفاده قرار می گیرد.
در پروژه ایجاد شده دستورات زیرا را اضافه کنید تا با استفاده از این دستورات اطلاعات پایگاه داده را واکشی کنیم و پس از آن بر روی این اطلاعات عملیات های خود را انجام دهیم.
#region Example Entity Framework Local
// Load some posts from the database into the context
context.Ablies.Where(p => p.PostName.Contains("Entity Framework")).Load();
// Get the local collection and make some changes to it
var localAbly = context.Ablies.Local;
localAbly.Add(new Ably { PostName = "What's New in EF" });
localAbly.Remove(context.Ablies.Find(1));
// Loop over the posts in the context.
Response.Write("In Local after entity-framework query: < br/>");
foreach (var ably in context.Ablies.Local)
{
Response.Write(string.Format(
"Found {0}: {1} with state {2} < br/>",
ably.Id,
ably.PostName,
context.Entry(ably).State));
}
var ably1 = context.Ablies.Find(1);
Response.Write(string.Format(
"State of post 1: {0} is {1}",
ably1.PostName,
context.Entry(ably1).State));
// Query some more posts from the database
context.Ablies.Where(p => p.PostName.Contains("MVC")).Load();
// Loop over the posts in the context again.
Response.Write("\nIn Local after MVC query: < br/>");
foreach (var post in context.Ablies.Local)
{
Response.Write(string.Format(
"Found {0}: {1} with state {2}< br/>",
post.Id,
post.PostName,
context.Entry(post).State));
}
پس از اجرای این دستورات خروجی ما به صورت زیر خواهد بود:
In Local after entity-framework query:
Found 3: MVC & Entity Framework4 with state Unchanged
Found 5: MVC & Entity Framework5 with state Unchanged
Found 0: What's New in EF with state Added
State of post 1: EF Beginners Guide is Deleted
In Local after asp.net query:
Found 3: MVC & Entity Framework4 with state Unchanged
Found 5: MVC & Entity Framework5 with state Unchanged
Found 0: What's New in EF with state Added
Found 4: ASP.NET MVC with state Unchanged
با اجرای دستور اول
context.Ablies.Where(p => p.PostName.Contains("Entity Framework")).Load();
تنها رکوردهایی بازیابی می شوند که کلمه Entity Framework در آنها وجود دارد سپس تمامی این رکورد ها توسط دستور Local درون localAbly ذخیره می شوند. در خط بعد یک رکورد جدید به رکوردهای Local اضافه می شود و همچنین در خط بعد از رکوردهای موجود Local، رکوردی با ID شماره یک را حذف می کنیم.
حال تمامی اطلاعات local خود را چاپ می کنیم. پس از چاپ این اطلاعات رکوردی با Id شماره یک را جستجو می کنیم و اطلاعات آن را در شی ably1 می ریزیم. در نهایت اطلاعات این شی را چاپ می کنیم، همانطور که مشاهده می کنید این شی در وضعیت Deleted قرار دارد.
در بخش بعد پس از اجرای دستور زیر تمامی رکوردهایی که PostName آنها شامل MVC است را از پایگاه داده واکشی می کنیم و آنها را چاپ می کنیم.
context.Ablies.Where(p => p.PostName.Contains("MVC")).Load();
در این بخش اتفاق خاصی نیافتاده فقط رکوردهایی که واکشی کرده ایم متفاوت شده اند و همچنین رکوردی که جدید اضافه کردیم ("What's New in EF") با همان وضعیت قبی خود یعنی Added نمایش داده شده است.
رکورد جدید اضافه شده " What's New in EF" ابتدا به Collection مربوط به Context اضافه می شود و پس از اجرای دستور SaveChanges این رکورد به پایگاه داده اضافه خواهد شد. همچنین رکورد " EF Beginners Guide" به عنوان رکوردی که می بایست حذف شود در Context علامت گذاری شده است و این رکورد نیز پس از اجرای دستور SaveChanges از پایگاه داده حذف خواهد شد.
در اینجا ما دستور SaveChange را می توانیم اضافه کنیم و خروجی را مجددا بررسی کنیم. برای انجام این کار دستورات زیر را اجرا کنید.
#region Example Entity Framework Local & SaveChange
//Save change
context.SaveChanges();
// Get the local collection with "Entity Framework" Keyword
context.Ablies.Where(p => p.PostName.Contains("Entity Framework")).Load();
// Loop over the posts in the context.
Response.Write("In Local after entity-framework query: < br/>");
foreach (var ably in context.Ablies.Local)
{
Response.Write(string.Format(
"Found {0}: {1} with state {2} < br/>",
ably.Id,
ably.PostName,
context.Entry(ably).State));
}
// Query some more posts from the database with "MVC" Keyword
context.Ablies.Where(p => p.PostName.Contains("MVC")).Load();
// Loop over the posts in the context again.
Response.Write("\nIn Local after MVC query: < br/>");
foreach (var post in context.Ablies.Local)
{
Response.Write(string.Format(
"Found {0}: {1} with state {2}< br/>",
post.Id,
post.PostName,
context.Entry(post).State));
}
همانطور که در کد بالا مشاهده می کنید ما در ابتدا تمامی تغییرات را بر روی پایگاه داده خود ذخیره کردیم و سپس با استفاده از دستور
context.Ablies.Where(p => p.PostName.Contains("Entity Framework")).Load();
تمام رکوردهایی که کلمه Entity Framework در آن قید شده است را Load کردیم و در نهایت توسط یک حلقه این رکورد ها را چاپ کردیم.
در بخش بعد نیز مجددا تمامی رکوردهایی که کلمه MVC در آن قید شده است را به صورت زیر Load کردیم.
context.Ablies.Where(p => p.PostName.Contains("MVC")).Load();
این رکوردها را نیز مانند بخش قبل با استفاده از یک حلقه foreach چاپ کردیم.
خروجی ما به صورت زیر خواهد بود.
In Local after entity-framework query:
Found 3: MVC & Entity Framework4 with state Unchanged
Found 5: MVC & Entity Framework5 with state Unchanged
Found 0: What's New in EF with state Unchanged
In Local after asp.net query:
Found 3: MVC & Entity Framework4 with state Unchanged
Found 5: MVC & Entity Framework5 with state Unchanged
Found 0: What's New in EF with state Unchanged
Found 4: ASP.NET MVC with state Unchanged
اگر خروجی این مثال را با مثال قبل مقایسه کنید مشاهده خواهید کرد که رکورد زیر دیگر نمایش داده نمی شود، زیرا این رکورد به دلیل علامت گذاری شدن برای حذف پس از دستور SaveChanges از پایگاه داده ما حذف شده است.
State of post 1: EF Beginners Guide is Deleted
همچنین State رکورد زیر نیز از Added به Unchanged تغییر داده شده است، زیرا این رکورد نیز به پایگاه داده اضافه شده است.
Found 0: What's New in EF with state Added
نکته:
اگر در برنامه خود از تعداد زیادی Entity استفاده می کنید این روش و استفاده از دستورات Local پیشنهاد نمی شود زیرا واکشی این همه اطلاعات عملا مخالف بهینه سازی برنامه است اما برعکس اگر قرار است تعداد زیادی رکورد را تغییر دهید و یا اطلاعات زیادی را در یک زمان درج کنید می توانید ابتدا تمامی عملیات های خود را اجرا کنید و در نهایت تنها یک بار این تغییرات را به SQL Server ارسال کنید.
در خصوص اینکه چگونه می توانیم وضعیت این رکوردها را تغییر دهیم و سایر مطالب مربوط به مدیریت Entry().State ها در بخش های بعدی صحبت خواهیم کرد.