Unit of Work چیست ؟

Unit of Work چیست ؟

Unit Of Work یکی از این concept های بسیار مهمی است که تقریبا تمامی توسعه دهنده ها با اون برخورد داشته و استفاده کرده اند. موضوعی که در اینجا وجود داره این هست که Unit Of Work فقط به یک صورت و یک جا آن هم با EF(یا ORMهای مشابه) و فقط به یک فرم بکار برده می شود.(به عنوان یک Facadeای از تمام Repositoryها !!) در این مقاله‏ ادامه ‏ی پرسش ‏های متداول الگوی طراحی بخش ۱ و ۲ و ۳ و۴ است.

اگر بخش قبلی مطلب ما را نخوانده‏ اید همواره می‏توانید آن را در لینک‏ های زیر بخوانید.

 فهرست مطالب Unit of Work

  •  کاربرد واحد Design Pattern کار کدام است؟
  • “کار” و “واحد” در یک برنامه ‏ی نرم ‏افزاری چیست؟
  • تراکنش‏ های منطقی! = CRUD فیزیکی
  • بنابراین، این مورد می‏تواند با استفاده از تراکنش ‏های ساده حاصل شود؟
  •  پس چگونه می‏توانیم به آن دست یابیم؟
  •  کد نمونه‏ ی C# برای واحد کار
  •  مرحله ۱: یک رابط تعمیم یافته (IEntity) برای اشیاء کسب و کار ایجاد کنید
  •  مرحله ۲: رابط “IEntity” را پیاده سازی کنید
  •  مرحله ۳: مجموعه‏ ی واحد کار را ایجاد کنید
  • مرحله ۴: کارکرد آن را ببینید
  • سورس کد واحد الگوی طراحی کار

کاربرد واحد الگوی طراحی کار کدام است؟

واحد الگوی طراحی کار دو کار مهم انجام می‏دهد:

نخست اینکه بروز رسانی‏ های درون حافظه ‏ای را کنترل می‏کند و دوم اینکه این بروز رسانی‏ های درون حافظه ‏ای را به عنوان یک تراکنش به پایگاه داده ارسال می‏کند.

بنابراین برای دستیابی به اهداف بالا دو مرحله را طی می‏کند:

فهرست ‏هایی از اشیاء کسب و کار که تغییر کرده‏ اند (درج شده، بروز رسانی شده، یا حذف شده) را در طول یک تراکنش بصورت درون حافظه ‏ای نگه می‏دارد.

هنگامیکه یک تراکنش کامل شد، تمامی این بروز رسانی ‏ها به عنوان یک واحد کار بزرگ ارسال می‏شوند تا به یکباره و بصورت فیزیکی در یک پایگاه داده نگهداری شوند.

“کار” و “واحد” در یک برنامه ‏ی نرم‏ افزاری چیست؟

یکی از تعاریف ساده ‏ی کار اجرای برخی وظایف است. از دید یک برنامه ‏ی نرم ‏افزاری، کار چیزی جز درج کردن، بروزرسانی، و حذف داده ‏ها نیست.

برای نمونه بیایید در نظر بگیریم که یک برنامه دارید که داده ‏های مشتری را در یک پایگاه نگه می‏دارد.

بنابراین هنگامیکه یک سابقه/رکورد مشتری در پایگاه داده اضافه، بروز رسانی، یا حذف می‏شود، یک واحد است. به زبان ساده، معادله اینطور است.


۱ customer CRUD = 1 unit of work

که CRUD نشان دهنده‏ی عملیات create (ایجاد)، read (خواندن)، update (بروز رسانی)، و delete (حذف) بر روی یک تک رکورد مشتری است.

Unit of Work

Unit of Work

تراکنش‏ های منطقی! = CRUD (فیزیکی)

معادله ‏ای که در بخش قبل مورد بحث قرار دادیم هنگامیکه صحبت از سناریوهای دنیای واقعی باشد بسیار تغییر می‏کند.

حال سناریوی زیر را در نظر بگیرید که در آن تمام مشتری‏ ها می‏توانند چندین آدرس داشته باشند.

سپس بسیاری از سطرها تبدیل به ۱ واحد کار خواهند شد.

برای مثال می‏توانید در تصویر زیر ببینید که مشتری “Shiv” دو آدرس دارد، بنابراین معادله برای سناریوی زیر بدین صورت است:

۳ Customer CRUD = 1 Logical unit of work

Unit of Work

Unit of Work

 

پس به زبان ساده، تراکنش ‏ها برای هر سه رکورد باید موفقیت‏ آمیز بوده یا همگی باید شکست بخورند.

باید اتمیک (ATOMIC) باشد. بعبارتی دیگر، بسیار محتمل است که تعداد زیادی عملیات CRUD برابر با ۱ واحد کار باشند.

بنابراین، این مورد می‏تواند با استفاده از تراکنش ‏های ساده حاصل شود؟

بسیاری از توسعه دهندگان می‏توانند نتیجه ‏گیری کنند که الزامات فوق می‏تواند با راه اندازی تمامی اعمال CRUD در یک تراکنش برآورده شود.

قطعا در پشت پرده از تراکنش ‏های پایگاه داده (یعنی شیء TransactionScope) استفاده می‏کند.

اما واحد کار بسیار فراتر از تراکنش ‏های ساده‏ی پایگاه داده است، تنها تغییرات را به پایگاه داده ارسال می‏کند و نه تمام سطرها را.

بگذارید همین مطلب را با جزئیات بیشتر به شما توضیح دهم.

بیایید در نظر بگیریم برنامه ‏ی شما سه رکورد را از پایگاه داده بازیابی می‏کند.

اما تنها دو رکورد را، همانطور که در تصویر زیر نشان داده شده، اصلاح می‏کند.

بنابراین تنها رکوردهای اصلاح شده/تغییر یافته به پایگاه داده ارسال می‏شوند و نه تمام رکوردها. این کار جابجایی‏ های فیزیکی پایگاه داده را بهینه کرده و اینگونه کارایی را افزایش می‏دهد.

Unit of Work

Unit of Work

به زبان ساده، معادله‏ ی نهایی واحد کار بدین صورت است:

۱ Unit of work = Modified records in a transaction

پس چگونه می‏توانیم به آن دست یابیم؟

برای دستیابی به آن، اولین چیزی که به آن نیاز داریم یک مجموعه ‏ی درون حافظه‏ ای است.

در این مجموعه، تمام اشیاء کسب و کار را اضافه خواهیم کرد.

حال هنگامیکه تراکنش در برنامه اتفاق می‏افتد، در این مجموعه‏ ی درون حافظه ‏ای ردیابی خواهند شد.

هنگامیکه برنامه تمامی موارد را کامل کرد، این اشیاء کسب و کار تغییر یافته را در “یک تراکنش” به پایگاه داده ارسال خواهد کرد.

به عبارتی دیگر، یا همه‏ ی آن‏ها تثبیت می‏شوند یا همه شکست می‏خورند.

Unit of Work

Unit of Work

کد نمونه‏ ی سی شارپ برای واحد کار

مرحله ۱: یک رابط تعمیم یافته (IEntity) برای اشیاء کسب و کار ایجاد کنید.

در پایان روز، یک واحد کار چیزی جز یک مجموعه که تغییرات بر روی اشیاء کسب و کار را نگهداری و ردیابی می‏کند نیست.

حال در یک برنامه می‏توانیم تعداد زیادی شیء کسب و کار با انواع مختلف داشته باشیم.

برای مثال می‏توانید مشتری، تأمین کننده، حساب‏ها و … داشته باشید.

به منظور قابل استفاده‏ ی مجدد کردن مجموعه در هر شیء کسب و کاری، بیایید تمامی اشیاء کسب و کار را تنها به عنوان “موجودیت‏ ها” (Entities) تعمیم دهیم.

Unit of Work

Unit of Work

با این رویکرد، این مجموعه را با هر شیء کسب و کاری ، قابل استفاده‏ ی مجدد کرده و اینگونه از هر نوع کد تکراری پرهیز می‏کنیم.

بنابراین قدم اول ایجاد یک رابط تعمیم یافته به نام IEntity است که یک شیء کسب وکار را در پروژه ‏ی ما نشان می‏دهد.

این رابط IEntity یک خاصیت ID و متدهایی (درج، بروز رسانی، حذف، و بارگذاری) خواهد داشت که به ما در انجام عملیات CRUD بر روی شیء کسب و کار کمک خواهند کرد.

خاصیت ID یک عدد منحصر بفرد است که به ما در تشخیص رکورد در پایگاه داده بصورت منحصر بفرد کمک می‎کند.

public interface IEntity
{
    int Id { set; get; }
    void Insert();
    void Update();
    List<IEntity> Load();
}  

Unit of Work

Unit of Work

مرحله ۲: رابط “IEntity” را پیاده سازی کنید.

قدم بعدی پیاده سازی IEntity در تمام اشیاء کسب و کارمان است.

برای مثال در ادامه می‏توانید یک شیء کسب و کار Customer ساده را ببینید که رابط IEntity را پیاده سازی می‏کند.

همچنین تمامی اعمال CRUD را نیز پیاده سازی می‏کند.

می‏توانید مشاهده کنید که لایه‏ ی دسترسی به داده را برای پیاده سازی اعمال درج، بروز رسانی، و بارگذاری فراخوانی می‏کند.


public class Customer : IEntity
{
    private int _CustomerCode = 0;
    public int Id
    {
        get { return _CustomerCode; }
        set { _CustomerCode = value; }
    }
    private string _CustomerName = "";
    public string CustomerName
    {
        get { return _CustomerName; }
        set { _CustomerName = value; }
    }
    public void Insert()
    {
        DataAccess obj = new DataAccess();
        obj.InsertCustomer(_CustomerCode, CustomerName);
    }
    public  List<IEntity> Load()
    {
        DataAccess obj = new DataAccess();
        Customer o = new Customer();
        SqlDataReader ds = obj.GetCustomer(Id);
        while (ds.Read())
        {
            o.CustomerName = ds["CustomerName"].ToString();
        }
        List<IEntity> Li = (new List<Customer>()).ToList<IEntity>();
        Li.Add((IEntity) o);
        return Li;
    }
    public void Update()
    {
        DataAccess obj = new DataAccess();
        obj.UpdateCustomer(_CustomerCode, CustomerName);
    }
} 

مرحله ۳: مجموعه ‏ی واحد کار را ایجاد کنید.

قدم بعدی ایجاد کلاس مجموعه ‏ی واحد کار است.

بنابراین در ادامه یک کلاس ساده به نام SimpleExampleUOW ایجاد کرده ‏ایم.

این کلاس دو مجموعه‏ ی عمومی که به عنوان یک متغیر در سطح کلاس تعریف شده ‏اند دارد:

Changed و New. مجموعه‏ ی عمومی Changed موجودیت‏ هایی که برای بروز رسانی هستند را ذخیره خواهد کرد.

مجموعه‏ ی عمومی New موجودیت‏ های کسب و کاری که رکوردهای تازه / جدید هستند را خواهد داشت.

لطفا به خاطر داشته باشید که در این نمونه ‏ی نمایشی، قابلیت حذف را پیاده سازی نکرده ‏ام.

آن را به عنوان تکلیف به شما واگذار می‏کنم


public class SimpleExampleUOW
{
    private List<T> Changed = new List<T>();
    private List<T> New = new List<T>();
    …..
    …..
}  

ما همچنین متدهای Add و Load که مجموعه ‏های New و Changed را بارگذاری می‏کنند، به ترتیب پیاده سازی کرده ‏ایم.

اگر شیء از طریق متد Add وارد شود، به مجموعه‏ ی New اضافه می‏شود.

اگر از پایگاه داده بارگذاری شود در مجموعه ‏ی Changed بارگذاری خواهد شد.

public void Committ()
{
    using (TransactionScope scope = new TransactionScope())
    {
        foreach (T o in Changed)
        {
            o.Update();
        }
        foreach (T o in New)
        {
            o.Insert();
        }
        scope.Complete();
    }
}

حال تمامی این تغییرات که در مجموعه بروز رسانی شده ‏اند نیاز است تا به یکباره به پایگاه داده ارسال شوند.

بنابراین بدین منظور، یک تابع Commit نیز ایجاد کرده‏ ایم که بصورت حلقه ‏ای در مجموعه ‏های New و Changed پیش می‏رود و به ترتیب Insert و Update را فراخوانی می‏کند.

همچنین از شیء TransactionScope نیز برای اطمینان یافتن از اینکه تمامی این تغییرات به شیوه ‏ای اتمیک تثبیت شده ‏اند استفاده کرده ‏ایم.
شیوه‏ ی اتمیک بدین معناست که یا کل تغییرات در پایگاه داده بروز رسانی می‏شوند یا هیچکدام بروز رسانی نمی‏شوند.


public void Committ()
{
using (TransactionScope scope = new TransactionScope())
{
foreach (T o in Changed)
{
o.Update();
}
foreach (T o in New)
{
o.Insert();
}
scope.Complete();
}
}

در ادامه کد کامل SimpleExampleUOW آمده است:

 


public class SimpleExampleUOW
{
    private List<IEntity> Changed = new List<IEntity>();
    private List<IEntity> New = new List<IEntity>();
    public void Add(IEntity obj)
    {
        New.Add(obj);
    }
    public void Committ()
    {
        using (TransactionScope scope = new TransactionScope())
        {
            foreach (IEntity o in Changed)
            {
                o.Update();
            }
            foreach (IEntity o in New)
            {
                o.Insert();
            }
            scope.Complete();
        }    
    }
   public  void Load(IEntity o)
    {
        Changed  = o.Load() as List<IEntity>;
    }
} 

مرحله ۴: کارکرد آن را ببینید.

در سمت سرویس گیرنده، می‏توانیم شیء Customer را ایجاد کرده، اشیاء کسب و کار را در حافظه افزوده، و در نهایت تمامی این تغییرات به شیوه ‏ای اتمیک به پایگاه داده ‏ی فیزیکی از طریق فراخوانی متد Commit فرستاده می‏شوند.

Customer Customerobj = new Customer();// record 1 Customer
Customerobj.Id = 1000;
Customerobj.CustomerName = "shiv";

Supplier SupplierObj = new Supplier(); // Record 2 Supplier
Supplierobj.Id = 2000;
Supplierobj.SupplierName = "xxxx";

SimpleExampleUOW UowObj = new SimpleExampleUOW();
UowObj.Add(Customerobj); // record 1 added to inmemory
UowObj.Add(Supplierobj); // record 1 added to inmemory
UowObj.Committ(); // The full inmemory collection is sent for final committ 

زهره سلطانیان

نوشته‌های مرتبط

دیدگاه‌ها

*
*

این سایت از اکیسمت برای کاهش هرزنامه استفاده می کند. بیاموزید که چگونه اطلاعات دیدگاه های شما پردازش می‌شوند.