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 
زهره سلطانیان

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

دیدگاه‌ها

*
*

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

ما یک پرسشنامه‌ی کوچولو داریم، که قول میدیم وقتتون رو خیلی نمی‌گیره، ولی کلی به ما کمک می‌کنه، چون هم assessment مونه ,و هم شما به ما در این زمینه کمک میکنید.بزن بریم