آموزش Entity Framework (بخش سوم Tracking)

آموزش Entity Framework (بخش سوم Tracking)

بسم الله الرحمن الرحیم

آموزش Entity Framework

بهینه سازی Entity Framework

قبل از مطالعه این دوره آموزشی بهتر است دوره های زیر را نیز مطالعه کنید.

آموزش Code first

آموزش migration و Entity Framework در Code First

 

بخش اول 

بخش دوم

بخش سوم

 

 


در بخش قبل با مفاهیم Local و Load آشنا شدیم، همچنین قابلیت Entity states را به صورت مختصر معرفی کردیم. در این بخش قصد داریم با ویژگی Entity states بیشتر آشنا شویم.

 

Entity states در Entity Framework

در مقاله قبل گفتیم که Entity states می تواند یکی از 5 حالت زیر را داشته باشد.
•    Added
•    Unchanged
•    Modified
•    Deleted
•    Detached

 

Added

این وضعیت بدین معنا است که Entity شما به Context اضافه شده است اما به پایگاه داده هنوز اضافه نشده است.

 

Unchanged

این وضعیت بدین معنا است که Entity شما در Context با Record متناظر در پایگاه داده یکسان است و به نوعی در این اطلاعات تغییری ایجاد نشده است.

 

Modified

این وضعیت بدین معنا است که یکی و یا تمامی Property های Entity مورد نظر در Context تغییر داده شده است اما در پایگاه داده ثبت نشده است. این وضعیت برای Entity هایی است که در پایگاه داده قبلا اضافه شده اند.

 

Deleted

این وضعیت بدین معنا است که Entity مورد نظر از Context حذف شده است اما Record متناظر این Entity همچنان در پایگاه داده موجود است

 

Detached

این وضعیت بدین معنا است که Entity مورد نظر شما توسط Context بررسی و یا track نمی شود


همانطور که در مقاله قبل بررسی کردیم برای ثبت تغییرات انجام شده در Context بر روی سرور می بایست از دستور SaveChanges استفاده کنیم، این دستور باعث تغییر وضعیت Entity ها به صورت زیر می شود:

 

 

 

 

 

 

 

 

بسم الله الرحمن الرحیم

آموزش Entity Framework

بهینه سازی Entity Framework

قبل از مطالعه این دوره آموزشی بهتر است دوره های زیر را نیز مطالعه کنید.

آموزش Code first

آموزش migration و Entity Framework در Code First

 

بخش اول 

بخش دوم

بخش سوم

 

 


در بخش قبل با مفاهیم Local و Load آشنا شدیم، همچنین قابلیت Entity states را به صورت مختصر معرفی کردیم. در این بخش قصد داریم با ویژگی Entity states بیشتر آشنا شویم.

 

Entity states در Entity Framework

در مقاله قبل گفتیم که Entity states می تواند یکی از 5 حالت زیر را داشته باشد.
•    Added
•    Unchanged
•    Modified
•    Deleted
•    Detached

 

Added

این وضعیت بدین معنا است که Entity شما به Context اضافه شده است اما به پایگاه داده هنوز اضافه نشده است.

 

Unchanged

این وضعیت بدین معنا است که Entity شما در Context با Record متناظر در پایگاه داده یکسان است و به نوعی در این اطلاعات تغییری ایجاد نشده است.

 

Modified

این وضعیت بدین معنا است که یکی و یا تمامی Property های Entity مورد نظر در Context تغییر داده شده است اما در پایگاه داده ثبت نشده است. این وضعیت برای Entity هایی است که در پایگاه داده قبلا اضافه شده اند.

 

Deleted

این وضعیت بدین معنا است که Entity مورد نظر از Context حذف شده است اما Record متناظر این Entity همچنان در پایگاه داده موجود است

 

Detached

این وضعیت بدین معنا است که Entity مورد نظر شما توسط Context بررسی و یا track نمی شود


همانطور که در مقاله قبل بررسی کردیم برای ثبت تغییرات انجام شده در Context بر روی سرور می بایست از دستور SaveChanges استفاده کنیم، این دستور باعث تغییر وضعیت Entity ها به صورت زیر می شود:

 

Unchanged

با اجرای دستور SaveChanges بر روی Entity هایی که در وضعیت Unchanged هستند، هیچ اتفاقی نخواهد افتاد. همچنین در صورت اجرای دستور Update نیز این Entity ها به پایگاه داده ارسال نخواهند شد.

 

Added

با اجرای دستور SaveChanges بر روی Entity هایی که در وضعیت Added هستند، این Entity ها در پایگاه داده Insert می شوند و پس از آن وضعیت آنها به Unchanged تغییر داده می شود.

 

Modified

با اجرای دستور SaveChanges بر روی Entity هایی که در وضعیت Modifiedهستند، این Entity ها در پایگاه داده Update می شوند و پس از آن وضعیت آنها به Unchanged تغییر داده می شود.

 

Deleted

با اجرای دستور SaveChanges بر روی Entity هایی که در وضعیت Deleted هستند، این Entity ها از پایگاه داده حذف می شوند و پس از آن وضعیت آنها به detached تغییر داده می شود.
 

 

مدیریت Entity Framework و پایگاه داده


یکی از روش های رایج اضافه کردن یک رکورد جدید به پایگاه داده به صورت زیر است.

 

using (var context = new BloggingContext()) 
{ 
    var blog = new Blog { Name = "ADO.NET Blog" }; 
    context.Blogs.Add(blog); 
    context.SaveChanges(); 
}


علاوه بر این روش می توان به صورت زیر نیز عمل نمود:

 

using (var context = new BloggingContext()) 
{ 
    var blog = new Blog { Name = "ADO.NET Blog" }; 
    context.Entry(blog).State = EntityState.Added; 
    context.SaveChanges(); 
}

 

شما می توانید روابط بین Entity ها را نیز پیاده سازی کنید برای این کار می بایست ابتدا Entity مورد نظر خود را واکشی و سپس از طریق Property های مربوطه Entity جدید را به آن اضافه کنید.

 

using (var context = new BloggingContext()) 
{ 
    // Add a new User by setting a reference from a tracked Blog 
    var blog = context.Blogs.Find(1); 
    blog.Owner = new User { UserName = "Esmaeil Sheidaei" }; 
 
    // Add a new Post by adding to the collection of a tracked Blog 
    var blog = context.Blogs.Find(2); 
    blog.Posts.Add(new Post { Name = "How to Add Entities" }); 
 
    context.SaveChanges(); 
}

 

در مثال بالا ابتدا یک Entity را پیدا کرده ایم و سپس از طریق Property این Entity، یک Entity جدید را به آن متصل کرده ایم و در نهایت این Entity را ذخیره کرده ایم.

علاوه بر روش بالا یکی از روش های دیگر استفاده کردن از ویژگی Tracking است که در مقالات گذشته تا حدودی آن ها معرفی کردیم. Entity Framework با استفاده از Tracking می تواند وضعیت فعلی هر Entity را تشخیص دهد و بر اساس آن عملیات مناسبی را اتخاذ کند.

 

Tracking در Entity Framework

Attaching

فکر کنید رکوردی با Id شماره یک در پایگاه داده ما موجود است و ما در Context خود این Entity را Load نکرده ایم. اگر بخواهیم این Entity را نیز به Context خود اضافه کنیم. می توانیم به صورت زیر عمل کنیم.

 

var existingBlog = new Blog { BlogId = 1, Name = "Ably.ir Blog" }; 
 
using (var context = new BloggingContext()) 
{ 
    context.Blogs.Attach(existingBlog); 
 
    // Do some more work...  
 
    context.SaveChanges(); 
}

 

در کد بالا ما Id و Name یک رکورد موجود در پایگاه  داده را مشخص کرده ایم، existingBlog در حال حاضر معادل یک رکورد موجود ما در پایگاه داده است. حال می خواهیم این Entity و یا همان شی خود را به Context اضافه کنیم تا از این به بعد Context وضعیت این شی را نیز Track کند.
اگر ما مقادیر این Entity را تغییر ندهیم و context.SaveChanges را اجرا کنیم هیچ اتفاقی نخواهد افتاد زیرا Entity Framework پس از بررسی این شی مشاهده می کند که این شی دارای وضعیت unchanged می باشد پس نیازی به بروز رسانی پایگاه داده نمی بیند.

یک روش دیگر برای اتصال یا همان Attach کردن یک رکورد موجود در پایگاه داده به Context به صورت زیر است.

 

var existingBlog = new Blog { BlogId = 1, Name = "Ably.ir Blog" }; 
 
using (var context = new BloggingContext()) 
{ 
    context.Entry(existingBlog).State = EntityState.Unchanged; 
    // Do some more work...  
 
    context.SaveChanges(); 
}

 

همانطور که مشاهده می کنید کد این مثال با مثال قبلا مشابه است و تنها تفاوت این دو مثال در دو خط زیر است


مثال اول

context.Blogs.Attach(existingBlog); 

 

مثال دوم

context.Entry(existingBlog).State = EntityState.Unchanged;


با اعلام صریح شما به Entity framework در خصوص اینکه وضعیت شی شما Unchanged است Entity framework متوجه خواهد شد که این یک رکورد موجود است و نیازی به ثبت مجدد این اطلاعات نیست.
اگر به کد این دو مثال کد زیر را قبل از context.SaveChanges اضافه کنید مشاهده می کنید که در هر دو مثال وضعیت شی existingBlog به صورت Unchanged است.

 

Response.Write(context.Entry(existingBlog).State.ToString());


نکته: اگر در اجرای این دو مثال خطای زیر را دریافت کردید، به این دلیل است که Entity Framework دلیل اجرای دستور context.SaveChanges را خطا می داند زیرا هیچ اطلاعاتی برای ثبت و یا بر روز رسانی وجود ندارد.

اگر بخواهیم خطای بالا را برطرف کنیم و یا به Entity Framework بگوییم که شی existingBlog را مجددا به پایگاه داده ارسال کنید کافی است کد خود  را به صورت زیر تغییر دهیم.

 

var existingBlog = new Blog { BlogId = 1, Name = "Ably.ir Blog" };
 
using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Modified;
 
    // Do some more work...  
 
    context.SaveChanges();
}

 

در این مثال نیز مشابه مثال های قبل عمل کردیم و تنها کدی که تغییر داده ایم، خط زیر می باشد.

 

context.Entry(existingBlog).State = EntityState.Modified; 

 

با اعلام صریح وضعیت یک Entity به صورت Modified به Entity Framework اعلام می کنیم که می بایست این شی را در پایگاه داده ثبت کند.

هنگامی که یک شی موجود را به Context اضافه می کنید به صورت پیش فرض مقدار EntityState این شی Unchanged می باشد به همین دلیل این شی به پایگاه داده ارسال نمی شود.
دلیل این مورد که وقتی یک شی به Context اضافه می شود مقدار پیش فرض Unchanged دریافت می کند این است که Context قبل از اضافه شدن این شی، آن را Track نمی کرده است و به همین دلیل اگر حتی شما مثلا در همین مثال مقدار Name متفاوتی با Name ثبت شده در پایگاه داده به شی خود بدهید باز این اطلاعات به پایگاه داده ارسال نخواهد شد اما اگر به صورت زیر عمل کنید Context شما متوجه خواهد شد که مقدار شی شما در Context با مقدار موجود همین شی در پایگاه داده متفاوت است و در نهایت به همین دلیل اطلاعات شما را ثبت خواهد کرد.
برای درک بهتر مثالی را بررسی می کنیم.
فکر کنید یک رکورد در Table پایگاه داده خود با Id شماره یک  داریم مقدار فیلد Name  این رکورد برابر "Ably.ir Blog" است حال اگر در کد خود مقدار Name متفاوتی بدهم انتظار داریم که Entity Framework این شی را در پایگاه داده بر روز کند.

 

var existingBlog = new Blog { BlogId = 1, Name = "Esmaeil Sheidaei" };
 
using (var context = new BloggingContext())
{
Response.Write(context.Entry(existingBlog).State.ToString());
context.Blogs.Attach(existingBlog);
context.SaveChanges();
}

 

اگر خروجی مثال را که چاپ کرده ایم بررسی کنید مشاهده خواهید کرد که Unchanged چاپ شده است در نتیجه Entity Framework نتوانسته شی شما را Track   کند. اگر پایگاه داده خود را نیز چک کنید مشاهده می کنید که رکورد شما بر روز نشده است.


راه حل

ابتدا با یک تغییر ساده مشکل بالا را حل می کنیم.

 

var existingBlog = new Blog { BlogId = 1, Name = "Esmaeil Sheidaei" };
 
using (var context = new BloggingContext())
{
Response.Write(context.Entry(existingBlog).State.ToString());
context.Blogs.Attach(existingBlog);
context.SaveChanges();  //new Strat Tracking…. For entity in your context
Blog.Name="Esmaeil Sheidaei";
Response.Write(context.Entry(existingBlog).State.ToString());
context.SaveChanges();  
}

 

کد بالا با مثال قبل مشابه است ما تنها یک تغییر کوچک به وجود آورده ایم.
اگر به ابتدای این مقاله مراجعه کنید 5 State ایی که هر Entity می تواند دریافت کند را توضیح دادیم و گفتیم اگر context.SaveChanges اجرا شود هر یک از این State ها به چه State ایی تبدیل می شوند.
وقتی این تبدیل State صورت می گیرد در واقعه یعنی اینکه Entity Framework به پایگاه داده مراجعه می کند و تمامی اشیا درون Context را با رکوردهای متناظرشان در پایگاه داده (در صورت وجود) بررسی می کند و تغییرات لازم مربوط به Insert,Update,Delete را انجام می دهد و دوباره Tracking خود را آغاز می کند. در نتیجه از این لحظه به بعد مجددا می داند چه شی تغییر کرده و یا خیر.
ما از همین ویژگی استفاده کرده ایم و یک بار Context خود را Save کرده ایم و دوباره مقدار Name را تغییر داده ایم در خط زیر Entity Framework این بار متوجه می شود که این رکورد بر روز شده است و به همین دلیل در context.SaveChanges خط آخر اطلاعات شما بر روز می شود. خروجی این مثال به صورت زیر است:

 

Unchanged
Modified

 

دستور بالا دستور صحیحی نیست زیرا شما تمامی اطلاعات Context خود را مجددا Save می کنید و این سربار زیادی دارد یک راه حل ساده و صحیح کد تغییر کد بالا به صورت زیر است

 

var existingBlog = new Blog { BlogId = 1, Name = "Ably.ir Blog" };
 
using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Modified;
 
    // Do some more work...  
 
    context.SaveChanges(); 

}

 

در این مثال به دلیل قید کردن دستور زیر Entity Framework متوجه می شود که شما میخواهید این رکورد در پایگاه داده بر روز شود در نتیجه تمامی اطلاعات شما بدون اینکه بداند کدام یک تغییر کرده است در رکورد قبلی جایگزین می کند.

 

context.Entry(existingBlog).State = EntityState.Modified; 

 

اما بر عکس مثال و توضیحات بالا ممکن است در بعضی مواقع بخواهید یک شی را که در حال Track شدن توسط Entity Framework است، و به عنوان یک شی تغییر داده شده که نیازمند بر روز رسانی (Insert,Update,Delete) است را از اعمال تغییر بر روی پایگاه داده جلوگیری کنید کافی است State این شی را به  Unchanged تغییر دهید در این صورت Entity Framework این شی را به عنوان یک شی که نیازمند ارسال به پایگاه داده نیست می شناسد.
مثال

 

var existingBlog = new Blog { BlogId = 1, Name = "ASP.NET MVC Blog" };
 
using (var context = new BloggingContext())
{
    context.Blogs.Attach(existingBlog);
    context.Entry(existingBlog).State = EntityState.Unchanged;
 
    // Do some more work...  
 
    context.SaveChanges();
}

 

طبیعتا تا این مرحله دقیقا متوجه شده اید که Entity Framework چطور تشخیص می دهد نیاز به ثبت و یا بر روز رسانی یک شی دارد یا خیر.
دقیقا اگر یک شی در Context شما دارای یکی از وضعیت های Added و Modified و همچنین Deleted باشد Entity Framework متوجه می شود که این شی را برای اعمال تغییرات باید به پایگاه داده ارسال کند.
بر اساس همین مقوله یک روش ساده برای پیاده سازی Method های Insert و Update به صورت زیر است

 

public void InsertOrUpdate(Blog blog)
{
    using (var context = new BloggingContext())
    {
        context.Entry(blog).State = blog.BlogId == 0 ?
                                   EntityState.Added :
                                   EntityState.Modified;
 
        context.SaveChanges();
    }
}

 

در این کد ما به این صورت عمل می کنیم که اگر یک شی جدید در برنامه ما ایجاد شود می بایست id شماره صفر داشته باشد و هنگامی که به پایگاه داده ارسال شد Id منحصر به فردی توسط خود SQL به رکورد ما اختصاص داده شود.
بر اساس سناریو بالا اگر رکوردی Id شماره صفر دارد باید Insert شود در غیر این صورت، ما می خواهیم یک رکورد را Update کنیم.
مبحث Tracking Entity Framework در همین جا به پایان می رسد.
در بخش های بعد انشالله در خصوص سایر مباحث بهینه سازی در Entity Framework صحبت خواهیم کرد
 

نظرات

  • Hannah Martinez
    حمزه
    دو شنبه 11 دی 1278 - 0:00

    سلام

    خیلی توضیحات خوب و مفیدی بود.

    من از روش شما استفاده کردم و در متد پیاده سازی کردم ولی یک مشکلی بر خوردم در وضعیت Update که همه فیلدها بروز رسانی میشود و فیلدهایی که مقدار دهی نمیشوند را null میکند و متوجه نشدم

    چطوری باید تغییرش بدم

نظرات یا سوالات خودرا با ما درمیان بگذارید

0912 097 5516 :شماره تماس
0713 625 1757 :شماره تماس