محتویات سایت
        برچسب های محبوب 








 
   استفاده از کلاس های تولید شده توسط Entity Framework CodeFirst در سرویس های WCF
  استفاده از کلاس های تولید شده توسط Entity Framework CodeFirst در سرویس های WCF
   LINQ
   ۱۱۸۰۸
   این مقاله حاوی فایل ضمیمه نمی باشد
   مرتضی صحراگرد
   ۱۳۹۱/۲/۲۸
نسخه قابل چاپ نسخه قابل چاپ

تذکر:

جهت درک صحیح مطالب این مقاله، آشنایی با مفاهیم Entity Framework CodeFirst و WCF الزامی می باشد.

همانطور که حتما مستحضر هستید، شرکت مایکروسافت در نسخه Entity Framework 4.1 برای اولین بار کلاس DbContext را معرفی نمود که شیوه نوینی جهت برنامه نویسی برای پایگاه داده بوده و با نام  CodeFirst شناخته می شود. این شیوه برنامه نویسی بسیار مورد استقبال برنامه نویسان گرفت و در نسخه های بعدی امکانات جالب فراوانی به آن اضافه شد و هم اکنون نیز در حال توسعه و بهینه سازی می باشد.

یکی از مشکلات کار با ابزار های ORM این می باشد که کلاس های تولید شده معمولا از کلاس های پایه ای به ارث رفته اند و این موضوع باعث می شود که این کلاس ها به سادگی قابل انتقال از سمت سرویس به کلاینت نباشند و هزینه سریالایز (Serialize) و دی سریالایز (Deserialize) نمودن این کلاس ها نیز بسیار بالا می باشد.

بنابراین این موضوع همواره مورد علاقه برنامه نویسان بوده است که بتوانند از کلاس های پایه خود (POCO) و بدون هیچ سربار اضافی به جای کلاس های پیشفرض تولید شده توسط Entity Framework استفاده نمایند.

با استفاده از روش CodeFirst براحتی می توان این قابلیت را ایجاد نمود. برای درک بهتر مسئله، به یک مثال توجه کنید.

فرض کنید که دو کلاس به نام های Customer و Address داریم و به ازای این دو کلاس، دو جدول در پایگاه داده با همین نام ها وجود دارد. رابطه بین جدول Customer با جدول Address از نوع "یک به چند" می باشد یعنی هر مشتری می تواند چندین آدرس داشته باشد.

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

public partial class CustomerDBEntities : DbContext

{

    public DbSet<Address> Addresses { get; set; }

    public DbSet<Customer> Customers { get; set; }

}

 

 

public  class Customer

{

    public Customer()

    {

        this.Addresses = new HashSet<Address>();

    }

 

    public int CustomerId { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

 

    public ICollection<Address> Addresses { get; set; }

}

 

public  class Address

{

    public int AddressId { get; set; }

    public int CustomerId { get; set; }

    public string Street { get; set; }

    public string Alley { get; set; }

    public int No { get; set; }

 

    public Customer Customer { get; set; }

}

همانطور که در بالا ملاحظه می کنید، کلاس های Customer و Address از هیچ کلاس دیگری به ارث نرفته اند و دارای ویژگی های اضافه دیگری نیز نیستند و کاملا مناسب برای منظور ما یعنی استفاده در سرویس های WCF می باشند.

اما روش بالا چند محدودیت برای ما ایجاد کرده است. از جمله اینکه چون موجودیت ها از کلاس های پایه Entity Framework به ارث نرفته اند، پس قابلیت های Lazy Loading و Change Traking از کار افتاده اند و دیگر قابل استفاده نمی باشند.

برای اینکه قابلیت Lazy Loading را برای کلاس ها فراهم کنیم کافیست که خصوصیاتی ناوبری (Navigation Properties) را به شکل virtual در آوریم. لازم به ذکر است که خصوصیاتی ناوبری، همان خصوصیاتی می باشند که نمایانگر ارتباط بین کلاس ها (و احتمالا جداول پایگاه داده) می باشند. در مثال ما هر مشتری می تواند چندین آدرس داشته باشد و هر آدرس حتما مربوط به یک مشتری می باشد. بنابراین خصوصیت Addresses در کلاس Customer و خصوصیت Customer در کلاس Address نمایانگر این رابطه بوده و خصوصیاتی ناوبری می باشند.

تغییرات ما برای کلاس های Customer و Address به شکل زیر اعمال شده است.

public  class Customer

{

    public Customer()

    {

        this.Addresses = new HashSet<Address>();

    }

    public int CustomerId { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

 

    public virtual ICollection<Address> Addresses { get; set; }

}

 

public  class Address

{

    public int AddressId { get; set; }

    public int CustomerId { get; set; }

    public string Street { get; set; }

    public string Alley { get; set; }

    public int No { get; set; }

 

    public virtual Customer Customer { get; set; }

}

خوب تا اینجای کار همه چیز خوب پیش رفته و قابلیت Lazy Loading نیز به کلاس های ما اضافه شده است.

شاید این سوال در ذهنتان بوجود آمده باشد که چگونه فقط با اضافه نموده کلمه کلیدی virtual این قابلیت به کلاس های ما اضافه می شود؟! 

با virtual نمودن خصوصیات ناوبری، Entity Framework هنگام اجرای برنامه (RunTime) کلاس هایی با عنوان "پروکسی های پویا" یا همان Dynamic Proxies به کلاس های Address و Customer اضافه می کند و بنابراین قابلیت Lazy Loading برای این کلاس ها در زمان اجرای برنامه فراهم می گردد.

تا اینجای کار توانستیم با تغییر کوچکی در کلاس ها قابلیت Lazy Loading را فراهم کنیم ولی با اضافه شدن پروکسی های پویا به کلاس های ما، مجددا مشکل ابتدای مقاله پدیدار می شوند و این کلاس ها قابلیت انتقال خود از طریق سرویس های WCF را از دست می دهند زیرا پروکسی های پویا به طور پیشفرض قابلیت سریالایز و دیسریالایز شدن را ندارند!

تذکر:

قبل از این که به سراغ راه حل برویم بد نیست به این موضوع نیز اشاره کنیم که در صورتی که تمامی خصوصیات کلاس های خود را virtual کنیم، قابلیت Change Tracking نیز به کلاس های ما اضافه می شود ولی اضافه نمودن این قابلیت بر روی راندمان و کارایی برنامه تاثیر می گذارد و بنابراین در صورتی که قصد انجام این کار را دارید، قبل از انجام این کار، تحقیقات و آزمایشات کافی را انجام دهید.

برگردیم به سراغ مشکل اصلی این مقاله.

خوشبختانه با استفاده کلاس DbContext می توانیم تولید کلاس های پروکسی را در زمان اجرای برنامه را فعال یا غیر فعال کنیم. بنابراین تنها کافیست که هر زمان نیاز به استفاده از آن ها در سرویس های WCF داشتیم، این قابلیت را به شکل زیر از کار بیندازیم. در اینصورت قابلیت Lazy Loading و برخی ویژگی های دیگر از کار افتاده و کلاس ها قابلیت استفاده در سرویس های WCF را خواهند داشت.

public Customer GetFirstCustomer()

{

    using(var context = new CustomerDBEntities())

    {

       context.Configuration.ProxyCreationEnabled = false;

 

        var firstCustomer = context.Customers.FirstOrDefault();

 

        return firstCustomer;

    }

}

در متد بالا ویژگی ProxyCreationEnabled مربوط به کلاس DbContext را غیر فعال نموده ایم و بنابراین پروکسی های داینامیک تولید نخواهند شد و نتیجه کوئری قابل استفاده در سرویس های WCF خواهد بود.

 

برچسب های مرتبط