استفاده از EF در اپلیکیشن های N-Tier : قسمت ششم
نویسنده: آرمین ضیاء
تاریخ: ۱۳۹۲/۱۱/۱۱ ۰:۲۵
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
[ServiceContract]
public interface IService1
{
[OperationContract]
Order InsertOrder();
[OperationContract]
void UpdateOrderWithoutRetrieving(Order order);
[OperationContract]
void UpdateOrderByRetrieving(Order order);
} public class Service1 : IService1
{
public Order InsertOrder()
{
using (var context = new EFRecipesEntities())
{
// remove previous test data
context.Database.ExecuteSqlCommand("delete from [orders]");
var order = new Order
{
Product = "Camping Tent",
Quantity = 3,
Status = "Received"
};
context.Orders.Add(order);
context.SaveChanges();
return order;
}
}
public void UpdateOrderWithoutRetrieving(Order order)
{
using (var context = new EFRecipesEntities())
{
try
{
context.Orders.Attach(order);
if (order.Status == "Received")
{
context.Entry(order).Property(x => x.Quantity).IsModified = true;
context.SaveChanges();
}
}
catch (OptimisticConcurrencyException ex)
{
// Handle OptimisticConcurrencyException
}
}
}
public void UpdateOrderByRetrieving(Order order)
{
using (var context = new EFRecipesEntities())
{
// fetch current entity from database
var dbOrder = context.Orders
.Single(o => o.OrderId == order.OrderId);
if (dbOrder != null &&
// execute concurrency check
StructuralComparisons.StructuralEqualityComparer.Equals(order.TimeStamp, dbOrder.TimeStamp))
{
dbOrder.Quantity = order.Quantity;
context.SaveChanges();
}
else
{
// Add code to handle concurrency issue
}
}
}
} class Program
{
static void Main(string[] args)
{
var service = new Service1Client();
var order = service.InsertOrder();
order.Quantity = 5;
service.UpdateOrderWithoutRetrieving(order);
order = service.InsertOrder();
order.Quantity = 3;
service.UpdateOrderByRetrieving(order);
}
} متد ()InsertOrder دادههای پیشین را حذف میکند، سفارش جدیدی میسازد و آن را در دیتابیس ثبت میکند. در آخر موجودیت جدید به کلاینت باز میگردد. موجودیت بازگشتی هر دو مقدار OrderId و TimeStamp را دارا است که توسط دیتابیس تولید شده اند. سپس در کلاینت از دو رویکرد نسبتا متفاوت برای بروز رسانی موجودیت استفاده میکنیم.
در رویکرد نخست، متد ()UpdateOrderWithoutRetrieving موجودیت دریافت شده از کلاینت را Attach میکند و چک میکند که مقدار فیلد Status چیست. اگر مقدار این فیلد "Received" باشد، فیلد Quantity را با EntityState.Modified علامت گذاری میکنیم و متد ()SaveChanges را فراخوانی میکنیم. EF دستورات لازم برای بروز رسانی را تولید میکند، که فیلد quantity را مقدار دهی کرده و یک عبارت where هم دارد که فیلدهای OrderId و TimeStamp را چک میکند. اگر مقدار TimeStamp توسط یک دستور بروز رسانی تغییر کرده باشد، بروز رسانی جاری با خطا مواجه خواهد شد. برای مدیریت این خطا ما بدنه کد را در یک بلاک try/catch قرار میدهیم، و استثنای OptimisticConcurrencyException را مهار میکنیم. این کار باعث میشود اطمینان داشته باشیم که موجودیت Order دریافت شده از متد ()InsertOrder تاکنون تغییری نکرده است. دقت کنید که در مثال جاری تمام خواص موجودیت بروز رسانی میشوند، صرفنظر از اینکه تغییر کرده باشند یا خیر.
رویکرد دوم نشان میدهد که چگونه میتوان وضعیت همزمانی موجودیت را پیش از بروز رسانی مشخصا دریافت و بررسی کرد. در اینجا میتوانید مقدار TimeStamp موجودیت را از دیتابیس بگیرید و آن را با مقدار موجودیت کلاینت مقایسه کنید تا وجود تغییرات مشخص شود. این رویکرد در متد ()UpdateOrderByRetrieving نمایش داده شده است. گرچه این رویکرد برای تشخیص تغییرات خواص موجودیتها و یا روابط شان مفید و کارآمد است، اما بهترین روش هم نیست. مثلا ممکن است از زمانی که موجودیت را از دیتابیس دریافت میکنید، تا زمانی که مقدار TimeStamp آن را مقایسه میکنید و نهایتا متد ()SaveChanges را صدا میزنید، موجودیت شما توسط کلاینت دیگری بروز رسانی شده باشد.
مسلما رویکرد دوم هزینه برتر از رویکرد اولی است، چرا که برای مقایسه مقادیر همزمانی موجودیت ها، یکبار موجودیت را از دیتابیس دریافت میکنید. اما این رویکرد در مواقعی که Object graphهای بزرگ یا پیچیده (complex) دارید بهتر است، چون پیش از ارسال موجودیتها به context در صورت برابر نبودن مقادیر همزمانی پروسس را لغو میکنید.