Archive for 2012/05

Dapper یک mini-ORM سریع

۱۳۹۱/۰۲/۲۹ ۱۹:۵۴ Salar Khalilzadeh https://plus.google.com/105397214522932500988 منتشر شده در تاریخ : ۱۳۹۱/۰۲/۲۹ دسته بندی : ، ، ، 3

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

به علت فشاری که سرویس قرار بود متحل بشه مشغول بهینه سازی عملکرد بخش های مختلف بودم. این فشار حداقل 100 درخواست در ثانیه از مجموع 10 کاربر همزمان برای شروع کار در نظر گرفته شده بود. در چنین حجم کاری هر میلی ثانیه نیز با ازش هست. در حین بررسی اجرای برنامه متوجه تاخیرهای بیش از حد و غیر عادی عملیات دیتابیس شدم. برای کاهش تاخیر سعی شد تا عملیات به روز رسانی و درج در دیتابیس غیر همزمان انجام شود(با Thread Pooling توسط BeginInvoke ساده)، اما این کار ممکنه ایجاد تداخل کنه و همیشه قابل انجام نیست.
در کل تاخیر های انجام شده توسط EntityFramework محسوس بودند. بنچمارک های انجام شده نیز این مطلب را نشان می دهد که EF کندتر از سایر orm ها عمل می کند. این کندی به چه معناست؟ یعنی برای انجام 500 درخواست متوالی EF حدود 600 میلی ثانیه زمان مصرف می کند، در حالی کار به ado.net ساده زمانی حدود 44 میلی ثانیه لازم دارد.
البته این برای برنامه های دسکتاب اصلا به چشم نخواهد آمد، چون اصلا کاربر بیش از چند نفر نمی شود.
در این بین مدل 3Tier که توسط این برنامه تولید می شود یک گزینه موجود بود اما مناسب نبود. این مدل نیاز به کار با StoredProcedures ها دارد، در حال حاظر مقدور نبود از sp ها استفاده کنم و همچنین وابستگی شدیدی به مدل(database model) خود دارد و چند محدودیت دیگر. در حین جستجو برای بهترین نتایج، Dapper جالب به نظر رسید.
Dapper یک نیمچه ORM که به علت سرعت بالایی که داره انتخاب من شد. کارکرد Dapper بسیار جالب هست، یک فایل را به پروژه اضافه می کنید و تمام. با استفاده از چند متن افزوده به connection دیتابیس به راحتی امکان دریافت نتایج و مپ کردن آنها به کلاسهای مدل دیتابیس را فراهم میکنه.
استفاده از آن هم به سادگی کد زیر هست:
using (var conn = new SqlConnection(myConnectionString)) {
     conn.Open();
     Account result = conn.Query<account>(@"SELECT * FROM Account WHERE Id = @Id", new {  Id = Id }).FirstOrDefault();
     ...
}
همانطور که می بینید با استفاده از عبارات sql فراخوانی ها مستقیما انجام میشود. پارامتر دوم پارامترهای کوئری است که از نوع dynamic تعریف شده و هر نوع شیئی را می پذیرد.
امضاهای مختلفی از این متد وجود دارد. در صورتی که نیاز باشد تا فقط چند ستون از جدول انتخاب شود از امضای زیر که خروجی dynamic دارد استفاده می کنیم:

dynamic account = conn.Query<dynamic>(@"SELECT Name, Address, Country FROM Account WHERE Id = @Id", new { Id = Id }).FirstOrDefault();
Console.WriteLine(account.Name);
Console.WriteLine(account.Address);
Console.WriteLine(account.Country);
براس آشنایی بیشتر با چند متدهای دیگر به صفحه Dapper مراجعه کنید.
کارایی dapper برای من سوال بود و زیاد به بنچ مارکهای دیگران اعتماد نداشتم به همین جهت به گفته ها بسنده نکرده و در یک تست ساده select بر آن شدم تا مقایسه ای انجام بدم. و این هم از نتایج:
Selecting 1 record in 200 iterations
------------------------
EF CodeFirst total: 00:00:00.2616611, ms: 261
ActiveRecord total: 00:00:00.1116575, ms: 111
Dapper total:       00:00:00.0177814, ms: 17
------------------------
EF CodeFirst total: 00:00:00.2460143, ms: 246
ActiveRecord total: 00:00:00.1105966, ms: 110
Dapper total:       00:00:00.0183922, ms: 18
------------------------
EF CodeFirst total: 00:00:00.2454677, ms: 245
ActiveRecord total: 00:00:00.1116689, ms: 111
Dapper total:       00:00:00.0173781, ms: 17
------------------------
EF CodeFirst total: 00:00:00.2448425, ms: 244
ActiveRecord total: 00:00:00.1117898, ms: 111
Dapper total:       00:00:00.0174709, ms: 17
------------------------
EF CodeFirst total: 00:00:00.2478316, ms: 247
ActiveRecord total: 00:00:00.1104544, ms: 110
Dapper total:       00:00:00.0197321, ms: 19
هر فراخوانی 200 بار تکرار شده و کل آزمون رو 5 بار تکرار کردم. همانطور که می بینید EntityFramework بدترین نتیجه رو بدست آورده و البته این همه اختلاف جای تعجب دارد. در این تست کار مستقیم با ado.net رو پوشش ندادم ولی با توجه به نتایج تست خود صفحه dapper در 500 تکرار در حد 4 یا 5 میلی ثانیه اختلاف بین dapper و ado.net مسقیم وجود دارد.

به همین دلایل یک الگو برای SalarDbCodeGenerator مهیا کردم که تا من رو از خطر نگهداری و نوشتن کدهای SQL تاحدودی راحت کنه. این الگو یک کلاس میانی برای هر جدول دیتابیس ایجاد می کنه که علاوه بر مدیریت اتصال های پایگاه داده (dapper مدیریت اتصال پایگاه داده را انجام نمی دهد) متد های اصلی CRUD را دارا است. همچنین برای ارتباطات جداول و کلیدهای اندیس و یکتا(unique) متدهای کمکی را تولید می کند.
خلاصه با استفاده از این الگو بدون نیاز به هر کاری می توان کارهای ساده با جداول را با قدرت Dapper انجام داد.
نکته: لازم نیست که حتما مدلهای جداول این الگو را تولید کنید، می توان از مدلهای سایر ORM ها با اندکی تغییر استفاده کرد.
نمونه کد:
using (var pdap = new PersonDap())
using (var transaction = pdap.BeginTransaction())
using (var cdap = new CarDap(pdap))
{
 var person = pdap.GetByPersonID(10);
 var carList = cdap.GetByPersonID(person.PersonID);

 var newCar = new Car()
     {
         CarPlaque = "1982-92",
         Color = "White",
         ModelType = "BMW",
         PersonID = person.PersonID,
     };
 cdap.Insert(newCar);
 transaction.Commit();

 var bmwList = pdap.Query<Car>(
  CarDap.SqlSelectCommand + " WHERE ModelType=@ModelType",
  new {ModelType = "BMW"})
  .ToList();

 ....
}
تنها نکته لازم به توضیح، ثابت SqlSelectCommand هست، این ثابت و سه ثابت دیگر دستورات sql پیشفرض برای عملیات CRUD در پایگاه داده هستند که توسط generator تولید شده و همیشه به روز هستند و در همه جداول وجود دارند. می توانید دردسر نوشتن دستورات sql را تا حدودی کاهش دهید.

و  در آخر بهتر است بدانید که Dapper توسط Sam Saffron یکی از برنامه نویسان سایت StackOverflow و برای همان سایت توسعه داده شده و بعدا open-source شده است.
  • لینکهای مفید

*پ.ن: نسخه جدید SalarDbCodeGenerator در سایت قرار گرفت که همراه با این مدل به نام DapperAccess است، یک به روزرسانی برای مدل EF دارد. از صفحه پروژه قابل دریافت است.

به روزرسانی: همانطور که در متن توضیح دادم Dapper یک ORM کامل نیست، برای اینکه شبهه ای ایجاد نشه، عنوان مطلب اصلاح شد.

 

معرفی SalarDbCodeGenerator

۱۳۹۱/۰۲/۱۶ ۹:۲۱ Salar Khalilzadeh https://plus.google.com/105397214522932500988 منتشر شده در تاریخ : ۱۳۹۱/۰۲/۱۶ دسته بندی : ، ، ، 7

مدتی بود که تصمیم داشتم پروژه رو که در ادامه مشاهده می کنید، اوپن سورس کنم. این برنامه در طول مدت تقریبا نزدیک به 4 سال به تدریج توسعه یافته و به شکل کنونی خود رسیده. تقریبا در بیشتر پروژه هایی که خودم شروع کردم از اون استفاده کردم و تا حد قابل قبولی آزمایش شده و قابل اعتماد هست.
SalarDbCodeGenerator یک تولید کننده کد است که از مدل اول دیتابیس (Database first) پیروی می کند. با استفاده از الگوهای از قبل تهیه شده پروژه را تولید کرده و آماده کامپایل و استفاده می شه. هر الگو معمولا شامل کلاسها مدلینگ جدوال، ویو ها و در نهایت کلاسهای پایه کار دیتابیس و یا کلاسهای پایه ORM مورد استفاده است.
از عمده مزیت استفاده از یک تولید کننده کد کاهش زمان مورد نیاز برای نوشتن کلاسهای مدل و توابع mapping است. تقریبا همه این کارها انجام شده و پروژه قابل استفاده تحویل می گیرید.
الگوهای مورد استفاده برنامه همگی قابلیت سفارشی سازی دارند و در حالت کلی برای نرم افزار زبان مورد استفاده شده هیچ اهمیتی ندارد و هر آنچه که در الگو معین شده خروجی را تعیین می کند. در نتیجه خروجی این تولید کننده در هر زبانی می تواند باشد.

پایگاه داده های مورد پشتیبانی
  • Micorsoft SQL Server (2000/2005/2008/2012)
    (پشتیبانی از توضیح برای فیلدها)
  • SQL Server Compact Edition 4
  • Oracle Database (8i/9i/10g/11g)
  • SQLite (v3 and above) 
خلاصه امکانات
  •  شناسایی کلید واژه های رزروشده (Keywords) زبان برنامه نویسی و عدم ایجاد تداخل کد تولیدی
  • پستیبانی از تغییر نامه جداول و ویوهای تولیدی جهت خوانا شدن در هنگام برنامه نویسی
  • افزودن توضیحات هر یک از فیلدهای جدول به مدلهای تولید شده جهت راحتی در هنگام کد نویسی (فقط SQLServer)
  • الگوی های سفارشی
  • استفاده شده از الگوهای کد به صورت XML که تغییر و ویرایش آنها را ساده می کند
  • تولید کد برای کلیدهای خارجی (Foreign keys) و اندیس ها (Index)

الگوهای همراه برنامه

Entity Framework Code First
  • EF4 CodeFirst
    (همراه با data annotations)
  • EF4 CodeFirst MVVM
  • (همراه با  data annotations و اعلان تغییر پراپرتی های mvvm)
  • EF4 CodeFirst Simple
    (کلاسهای ساده بدون ویژگی اضافه)
مدل دیتابیس را برای استفاده توسط Entity Framwork 4.2 CodeFirst تولید می کند. همچنین چندین متد و ویزگی اضافه که برای برنامه نویس مفید هستند همراه این مدل افزوده است.

data annotations و اعلان تغییر پراپرتی ها(INotifyPropertyChanged) به صورت دو الگوی جداگانه طراحی شده و در دسترس هستند. همچنین همه کلاسها به صورت partial بوده و قابلیت سفارش سازی دارند.
همراه با همه الگوهای پیش فرض معمولا کلاسهای Partial جداگانه ای تولید می شود تا برنامه نویس بتواند ویژگی ها و متد های سفارشی خودش را در آنها قرار دهد.

NHibernate Mapping By Code (در حال آماده سازی)
Mapping By Code از نسخه NHibernate 3.2 معرفی شد و بدون نیاز به داشتن فایلهای xml اضافه اجازه استفاده از مدلها را می دهد.
در زمانها فراقتم در حال آماده کردن این الگو هستم، که پس از آزمایش اطمینان از صحت عمل آن منتشر می کنم.

NHibernate ActiveRecord

  • NHibernate ActiveRecord
  • NHibernate ActiveRecord WCF
مدل های دیتابیس و کلاسهای مورد نیاز را تولید می کند که  توسط هر دو نسخه Castle ActiveRecord 2.2 یا 3 قابل استفاده است. Castle ActiveRecord از الگوی  ActiveRecord پیروی می کند و به صورت توکار از NHibernate برای اجرای کوئری ها استفاده میکند.
الگوی "NHibernate ActiveRecord WCF" اختصاصا برای کار با WCF بهینه سازی شده است، به این صورت که ID جدوال خارجی را نیز در خود نگه میدارد (این کار در حال عادی مرسوم نیست) جهت سهولت ارسال مدل ها در WCF. البته این Id ها باید به صورت دستی مقدار دهی شوند.

LinqToSql
نسخه متفاوتی از مدل Linq To Sql را تولید می کند. مدل تولید شده از روش برنامه های سه لایه تبعیت می کند. البته توجه شود که فقط دو لایه Bussinuess و DAL در این الگو تولید می شوند. طبق معمول جهت سفارشی سازی کلاسها  و متدهای اضافه مد تظر گرفته شده اند.

Entity Framework 4 POCO
  • EF4 POCO
  • EF4 POCO LINQ
مدل های دیتابیس و کلاسهای Entity Framework 4 را به صورت مدل POCO تولید می کند. POCO مخفف Plain Old Clr Object استفاده از روش سفارشی سازی شده برای Entity Framework نسخه 4 است.
الگوی "EF4 POCO LINQ" یک مدل اضافه روش برنامه نویسی سه لایه نیز تولید می کند، که هر دو روش در برنامه قابل استفاده هستند. جهت استفاده از این دو الگو باید حتما مراحل زیر را دنبال کنید.
ابتدا در پروژه باید حتما مدل ایجاد شود، نام مدل باید همنام دیتابیس به علاوه Entities باشد (مثلا TestDbEntities.edmx و TestDbEntities برای نام کلاس).
هنگام تهیه مدل حتما باید گزینه "Pluralize or singularize generated object names" حذف شود. و در نهایت ویرایشگر مدل (Model Designer) مقدار ویژگی "Code Generation Strategy" باید به "None" تغییر داده شود.
پس از طی این مراحل مدل تولیدی قابل استفاده خواهد بود.


3Tier
  • 3Tier CSharp
  • 3Tier VB.NET
مدل های دیتبایس را جهت استفاده در یک برنامه سه لایه کلاسیک تولید می کند. این الگوها از کلاسهای پایه ADO.Net جهت دسترسی به داده و انجام عملیات استفاده می کنند. همچنین همه عملیات پیش فرض این مدل ها توسط Stored Procedure تولید شده انجام می شود.
به همین دلایل سرعت عملیاتی این مدل بسیار بالا بوده و برای برنامه هایی که نیاز به سرعت زیادی دارند، بسیار مناسب می باشد.
الگوی "3Tier CSharp" علاوه بر موارد ذکر شده از یک مرحله caching اضافه هنگام خواندن اطلاعت بر روی ترتیب رکوردها نیر استفاده می کند. این باعث افزایش کارایی هنگام خواندن حجم عظیمی از داده می شود.
این دو الگو محدودیت های خودشان را نیز دارند، از جمله نیاز به نوشتن هر عملیات جدید به صورت دستی، عدم پشتیبانی از Linq و محدود بودن به SQL Server می توان اشاره کرد.

جهت اطلاعات بیشتر و مستندات الگوها می توانید به آدرس پروژه مراجعه کنید.
الگوها همگی به زبان XML تهیه شده اند و ویرایش آنها کار ساده ای است. در صورتی که درخواست کافی جهت تهیه مستندات برای قالب الگو دریافت کنم، این کار را خواهم کرد.

پنجره اصلی برنامه
پنجره تنظیمات برنامه

  • دانلود
این پروژه به صورت اوپن سورس و رایگان در آدرس salardbcodegenerator.codeplex.com قرار گرفته است.
همچنین کدهای برنامه به صورت آنلاین در این آدرس قابل مشاهده هستند.

پیشنهادات و ایرادات مشاهده شده از اینجا و یا صفحه نظرات پروژه مطرح کنید.