آموزش کتابخانه Microsoft Enterprise Library 5.0 - قسمت چهارم
پنج شنبه 16 آذر 1391 3:11 PM
برای استفاده از امکانات بلاک دیتا (Data Application Block) باید 4 اسمبلی زیر را به برنامه خود اضافه کنید. البته تحت شرایطی ممکن است نیاز به اضافه نمودن اسمبلی های دیگری نیز باشد که در آینده ملاحظه خواهید نمود.
تذکر بسیار مهم:
اسمبلی های مربوطه را باید از دایرکتوری نصب برنامه کپی کنید (یا ارجاع دهید). به طور مثال برای بنده این اسمبلی ها در مسیر زیر ذخیره شده اند.
هنگام کار با ابزار پیکربندی این کتابخانه، خصوصیاتی از قبیل PublicKeyToken و Version و ... تولید می شود که مختص اسمبلی هایی می باشد که به طور پیشفرض در این نسخه از کتابخانه تعبیه شده اند و هنگام نصب در محل دایرکتوری برنامه کپی می شوند.
اگر به خاطر بیاورید در مقالات قبلی هنگام شرح نحوه نصب این کتابخانه، دیدیم که در انتهای مراحل نصب، یک ورژن دیگر از این اسمبلی ها در همان زمان کامپایل شده و کنار محل ذخیره سازی سورس برنامه، ذخیره شدند. در صورتی که بخواهیم از این اسمبلی ها استفاده کنیم باید خصوصیت PublicKeyToken را تغییر داده و یا حذف نماییم. این مسائل مربوط به Strong Name بودن اسمبلی ها می شود و ما از بحث بیشتر در مورد آن خودداری می کنیم.
از میان 5 اسمبلی ذکر شده در قسمت بالا، چهار تای اول برای استفاده از تمامی بلاک ها الزامی می باشند ولی اسمبلی پنجم مختص بلاک دیتا می باشد.
برای استفاده از امکانات بلاک دیتا باید فضاهای نامی زیر را به برنامه اضافه کنید.
اگر بخواهید از مکانیزم تزریق وابستگی (Dependency Injection) که توسط بلاک Unity ایجاد شده است، استفاده نمایید باید فضاهای نامی زیر را نیز به برنامه اضافه کنید (ما در این سلسله مقالات از این روش استفاده نخواهیم کرد).
در نسخه های قبلی این کتابخانه، کلاس هایی به شکل Factory وجود داشتند که وظیفه نمونه سازی اشیاء را به عهده می گرفتند. به طور مثال کلاسی به نام DatabaseFactory وجود داشت (و البته هنوز هم وجود دارد!) که حاوی متدی به نام CreateDatabse بود. این متد یک نمونه شیء از کلاس Database را برمی گرداند که با استفاده این کلاس تمام اعمال مربوط به پایگاه داده انجام می شود. در قسمت زیر نحوه نمونه سازی کلاس Database توسط کلاس DatabaseFactory را ملاحظه می کنید.
پس از ارائه آخرین نسخه، استفاده از این روش منسوخ شده است. البته به منظور سازگار بودن با نسخه های پیشین همچنان این امکان وجود دارد که از این روش استفاده کنید ولی این عمل توصیه نمی شود و ما نیز از این روش استفاده نخواهیم نمود.
دو روش برای نمونه سازی اشیاء در کتابخانه Enterprise Library 5 توصیه شده است.
استفاده از Unity Service Locator (روش ساده تر)
استفاده از Unity Container (روش پیچیده تر)
به نحوه نمونه سازی کلاس Database با استفاده از روش Unity Service Locator دقت کنید.
نمونه سازی اشیاء در کلیه بلاک ها با استفاده از کلاس EnterpriseLibraryContainer انجام می شود. این کلاس دارای یک پراپرتی استاتیک به نام Current می باشد که این پراپرتی یک آبجکت از نوع IServiceLocator را برمی گرداند. اینترفیس IServiceLocator دارای یک متد جنریک به نام GetInstance می باشد که نام کلاس مورد تقاضای ما را (در اینجا Database) را دریافت نموده و عمل نمونه سازی از آن را انجام می دهد.
متد GetInstance دارای یک Overload دیگر نیز می باشد که نام رشته اتصال (Connection String) خاصی را که علاقه مند هستیم از آن استفاده کنیم را دریافت می کند. در قطع کد بالا با توجه به اینکه ما نام رشته اتصال پایگاه داده را به این متد نفرستاده ایم پس نمونه سازی با استفاده از رشته اتصال پیشفرض (که در فایل پیکربندی مشخص شده است) انجام می شود. ولی در صورت تمایل می توانید رشته اتصال مورد نظر خود را صریحا اعلام کنید تا نمونه سازی بر اساس آن انجام شود.
قبل از اینکه به معرفی اجمالی این روش بپردازیم باید بدانید که در حقیقت در روش Unity Service Locator نیز از Unity Container برای نمونه سازی اشیاء استفاده می شود ولی برای اینکه برنامه نویس به طور مستقیم درگیر کار با Unity Container نگردد، این عمل به شکل داخلی و پنهان انجام می شود و بنابراین شما فقط کافیست که فقط متد GetInstance را فراخوانی کنید.
در روش نمونه سازی اشیاء با استفاده از Unity Container شما امکان پیدا می کنید که به طور مستقیم با Unity Container که یک Dependency Injection Container می باشد، کار کنید. همانطور که قبلا هم بار ها ذکر کرده ایم، مبحث مربوط به تزریق وابستگی (Dependency Injection) خارج از اهداف این مقاله می باشد و ما قصد ورود به آن را نداریم. با استفاده از Unity Container شما می توانید حتی کلاس های داخلی برنامه خود را با استفاده از مکانیزم تزریق وابستگی نمونه سازی کنید که البته این مسئله نیاز به انجام اعمال بیشتری نیز دارد.
نمونه سازی اشیاء با استفاده از این روش به شکل زیر انجام می پذیرد.
ما در این سلسله مقالات از روش Unity Service Locator برای نمونه سازی اشیاء استفاده خواهیم نمود.
تمامی اعمال متداول CRUD (یعنی Create, Read, Update, Delete) توسط بلاک دیتا با استفاده از کلاس Database انجام می شود. کلاس Database از نوع abstract می باشد که برای پایگاه های داده مختلف، پیاده سازی های مختلفی از آن انجام شده است. از جمله این پیاده سازی ها می توان به SqlDatabase، SqlCeDatabase، GenericDatabase، OracleDatabase اشاره نمود.
معمولا توابعی که در میان تمامی پایگاه های داده عملکرد یکسانی دارند در کلاس Database تعریف شده اند و توابعی که مخصوص پایگاه داده مشخصی هستند در کلاسی که وظیفه پیاده سازی را به عهده گرفته است تعریف می شوند. به طور مثال توابعی که به شکل آسنکرون اجرا می شوند از قبیل BeginExecuteNonQuery و BeginExecuteReader و غیره خاص پایگاه داده Microsoft SQL Server می باشند و بنابراین در کلاس SqlDatabase تعریف شده اند و در نتیجه اگر بخواهید از این توابع استفاده کنید باید کلاس Database را به SqlDatabase تبدیل کنید. در صورتی که علاقه مند باشید می توانید شما نیز برای پایگاه داده دلخواه خود یک پیاده سازی از کلاس Database بنویسید.
کلاس Database دارای متدهای فراوانی می باشد که مهمترین آن ها را در شکل زیر ملاحظه می نمایید. با استفاده از این چند متد می توانیم تمامی اعمال متداول بر روی پایگاه داده را انجام دهیم.
هر یک از این متدها دارای Overload های متعددی می باشند و بنابراین هر یک از اعمال مربوط به پایگاه داده را می توان از چندین روش انجام داد. ما در این مقاله سعی می کنیم بهترین روش ها را معرفی نماییم.
فرض کنید جدولی به نام Student با ساختار زیر در پایگاه داده داریم که دارای تعدادی رکورد می باشد و قصد داریم اعمال متداول را بر روی آن انجام دهید.
کلاسی نیز به نام Studentداریم که برای نگهداری اطلاعات دانشجویان استفاده می شود.
انجام اعمال مختلف بر روی پایگاه داده معمولا از دو طریق انجام می پذیرد:
Overload های توابع کلاس Database به طور کلی به دو دسته تقسیم می شوند. دسته اول Overloadمی باشند که یک پارامتر های آن ها از نوع DbCommand می باشد و به وسیله این متدها همه نوع اعمالی را می توان انجام داد. دسته دوم Overload هایی هستند که نام یک پروسیجر یا یک دستور SQL را به همراه مجموعه ای از مقادیر (به عنوان مقادیر پارامتر ها) گرفته و عملیات را روی پایگاه داده انجام می دهند. Overload های نوع اول انعطاف پذیر تر هستند در حالیکه Overload های نوع دوم مقداری محدودیت دارند ولی در عوض استفاده از آن ها ساده تر می باشد. در ادامه مقاله و هنگام طرح مثال ها با این دو گروه بیشتر آشنا خواهید شد.
قبل از طرح مثال ها تاکید می شود که سورس کامل این مقاله از طریق لینک بالای صفحه قابل دریافت می باشد.
پروسیجر:
نوشتن متدی برای انجام این کار با استفاده از پروسیجر:
در روش متد بالا، مقادیری که قرار است برای پروسیجر ارسال شود را داخل آرایه ای از اشیاء به نام valuesریخته ایم. دقت کنید که ترتیب قرارگیری پارامتر ها باید با ترتیب دریافت پارامتر ها در پروسیجر یکسان باشد. در متد بالا ما ابتدا studentId و سپس age را اضافه کرده ایم. سپس یک نمونه سازی از نوع کلاس Database انجام داده ایم. همانطور که در مقالات قبلی ذکر شد، با توجه به اینکه در هنگام تعریف رشته اتصال پایگاه داده (Connection String) در فایل پیکربندی ما پارامتر providerName را برابر System.Data.SqlClient قرار داده ایم، نوع شیء ساخته شده برای کلاس Database از نوع SqlDatabase خواهد بود.
سپس با استفاده از یکی از Overload های متد ExecuteReader که نام پروسیجر را به عنوان پارامتر اول و مقادیر را به عنوان پارامتر دوم دریافت می کند، دستور خود را اجرا می کنیم. مقدار برگشتی متد ExecuteReader را به شکل IDataReader دریافت کرده ایم. درحقیقت IDataReader در اینجا از نوع SqlDataReader می باشد که در ADO.NET آن را به خوبی می شناسیم. در نهایت مقادیر را از IDataReader خوانده و یک شیء از Studentرا پر نموده ایم.
معادل متد بالا را سعی کنید با استفاده از ADO.NET بنویسید تا متوجه شوید چقدر کار ساده تر شده است. فرض کنید پروسیجر uspGetStudent تعداد زیادی پارامتر داشت. با استفاده از روش بالا تنها تفاوت این بود که سایر پارامتر ها را نیز باید به آرایه values اضافه می کردیم ولی در روش ADO.NET معمولی باید چندین خط کد برای اضافه نمودن پارامتر ها و مقادیر آن ها نوشته می شد. ضمنا عملیات باز و بسته نمودن اتصال پایگاه داده به بهترین وجه و داخل خود بلاک دیتا انجام می شود و شما اثری از آن را در قطعه کد بالا نمی بینید!
نوشتن متدی برای انجام این کار با استفاده از دستورات SQL:
در متد بالا ما از یک روش دیگر که همان استفاده از DbCommand می باشد، استفاده کرده ایم. ابتدا دستور sql را به متد GetSqlStringCommand ارسال کرده ایم تا شیء DbCommand مناسب برای دستور SQL ساخته شود. سپس با استفاده از متد AddInParameter مقادیر را به پارامتر ها نسبت داده ایم و در نهایت شیء DbCommand را برای اجرای دستور به متد ExecuteReader ارسال کرده ایم.
پروسیجر:
نوشتن متدی برای انجام این کار با استفاده از پروسیجر:
همانطور که ملاحظه می کنید، منطق کار تغییر چندانی نکرد و فقط ما به جای اینکه اطلاعات یک دانش آموز را بازیابی کنیم، اطلاعات تعدای از آن ها را بازیابی نموده ایم.
نوشتن متدی برای انجام این کار با استفاده از دستورات SQL:
در این متد هم تغییر چندانی ایجاد نکردیم.
در مثال های قبل ما مقادیری را برای پروسیجر یا دستور SQL ارسال می کردیم. در این مثال هیج مقداری را ارسال نمی کنیم و فقط قصد داریم لیست تمامی دانشجویان را بازیابی کنیم.
پروسیجر:
نوشتن متدی برای انجام این کار با استفاده از پروسیجر:
این بار از یک Overload متد ExecuteReader استفاده کرده ایم که CommandType.StoredProcedure را به عنوان پارامتر اول گرفته و نام پروسیجر را به عنوان پارامتر دوم دریافت می کند.
نوشتن متدی برای انجام این کار با استفاده از دستورات SQL:
دقیقا مانند متد بالا می باشد با این تفاوت که این بار CommandType را از نوع CommandType.Text تعیین می کنیم.
گاهی اوقات خروجی دستور SQL ما یک تک مقدار می باشد. هر چند از روش های گفته شده در بالا نیز می توان این عمل را انجام داد ولی با بهتر است که با عملکرد تابع ExecuteScalar نیز آشنا شوید.
پروسیجر:
نوشتن متدی برای انجام این کار با استفاده از پروسیجر:
ابتدا مقدار age را به طور پیشفرض برابر 1- تعیین کرده ایم تا اگر اصلا چنین دانش آموزی وجود نداشت متوجه آن شویم. عملکرد سایر قسمت های کد کاملا شفاف می باشد.
نوشتن متدی برای انجام این کار با استفاده از دستورات SQL:
عملکرد متد بالا نیز کاملا شفاف می باشد.
تاکنون تمامی اطلاعات را با استفاده از IDataReader بازیابی نموده ایم. اکنون قصد داریم اطلاعات را به صورت یک دیتاست بازیابی کنیم. برای انجام این کار در بلاک دیتا از دو متد ExecuteDataSet و LoadDataSet استفاده می شود. عملکرد این دو متد یکسان می باشد با این تفاوت که در متد ExecuteDataSet یک شیء دیتاست جدید ایجاد شده و برگردانده می شود در حالیکه در متد LoadDataSet بر روی دیتاستی که در حال حاضر موجود است (باید به عنوانی یک پارامتر برای این متد بفرستیم) تغییرات اعمال می شود.
نوشتن متدی برای انجام این کار با استفاده از پروسیجر:
در قطعه کد بالا از متد ExecuteDataSet استفاده شده است.
نوشتن متدی برای انجام این کار با استفاده از دستورات SQL:
برای بازیابی اطلاعات به شکل XML از متد ExecuteXmlReader استفاده می شود. این متد مخصوص پایگاه داده Microsoft SQL Server می باشد و بنابراین تعریف آن در کلاس SqlDatabase انجام شده است. برای استفاده از این متد باید کلاس Database را تبدیل به SqlDatabase کنیم. متدهای آسنکرون BeginExecuteXmlReader و EndExecuteXmlReader نیز در این کلاس تعریف شده اند که در صورت نیاز می توانید از آن ها استفاده کنید.
در قطعه کد زیر ما لیست مشخصات تمامی دانش آموزان را به شکل یک رشته XML بازیابی نموده ایم.
در دستور SQL قطعه کد بالا از عبارت FOR XML AUTO برای بازیابی شدن اطلاعات با فرمت XML استفاده کرده ایم و سپس برای دسترسی به این متد شیء Database را تبدیل به SqlDatabase کرده ایم. خروجی متد ExecuteXmlReader از نوع XmlReader می باشد که با پیمایش آن می توانیم اعمال مورد نظر خود را انجام دهیم.
تذکر: هنگام استفاده از متد ExecuteXmlReader حتما باید اتصال پایگاه داده (Connection) را صریحا ببندید. این متد پس از اجرا اتصال را نمی بندد و حتی XmlReader نیز هنگامی که Dispose می شود باز هم اتصال باز می ماند. ما در متد بالا در قسمت finally اتصال را بسته ایم.
محتویات متغیر result در قطع کد بالا مشابه زیر می باشد.
خوب، روش های مهم بازیابی اطلاعات را بررسی کردیم. اکنون به سراغ وارد نمودن اطللاعات جدید می رویم.
به طور معمول وارد نمودن اطلاعات در پایگاه داده یا با مقدار برگشتی (Output Value) می باشد و یا بدون آن. در این مثال بدون مقدار برگشتی را بررسی می کنیم.
پروسیجر:
نوشتن متدی برای انجام این کار با استفاده از پروسیجر:
ملاحظه می کنید که تنها تغییر این متد استفاده از ExecuteNonQuery می باشد و سایر قضایا همانند متدهای قبلی می باشد.
نوشتن متدی برای انجام این کار با استفاده از دستورات SQL:
پروسیجر:
نوشتن متدی برای انجام این کار با استفاده از پروسیجر:
با توجه به اینکه ما نیاز به دریافت پارامتر خروجی پروسیجر داریم، از روش DbCommand استفاده کرده ایم. دقت کنید که برای ساخت DbCommand این بار از متد GetStoredProcCommand استفاده نموده ایم چون قصد داریم شیء DbCommand با استفاده از پروسیجر ساخته شود. پارامتر خروجی را با استفاده از متد AddOutParameter اضافه کرده ایم. متد ExecuteNonQuery همانند مشابه خود در ADO.NET معمولی، پس از اجرا دستور، تعداد رکوردهایی که مورد تاثیر اجرای دستور قرار گرفته اند را برمی گرداند. در نهایت با استفاده از متد GetParameterValue مقدار پارامتر خروجی را بازیابی نموده ایم.
نوشتن متدی برای انجام این کار با استفاده از دستورات SQL:
روند کار شبیه متد قبلی می باشد با این تفاوت که این بار به جای پروسیجر از دستور SQL استفاده نموده ایم و از متد GetSqlStringCommand جهت ساخت شیء DbCommand استفاده کردیم.
بروز رسانی و حذف اطلاعات دقیقا همانند ورود اطلاعات جدید که در بالا بحث کردیم توسط متد ExecuteNonQuery انجام می شود با این تفاوت که محتوای دستور SQL یا پروسیجر از Delete یا Update استفاده می کند.
در هنگام کار با پایگاه داده گاهی نیاز است از Transaction استفاده کنیم. این موضوع معمولا زمانی اتفاق می افتد که نیاز داریم چندین تغییر در پایگاه داده ایجاد کنیم و این تغییرات وابسته به یکدیگر می باشند. اگر انجام یکی از تغییرات با شکست روبرو شود (و یا به هر نحوی نتیجه مور نظر ما حاصل نگردد) باید تمامی تغییرات بی اثر (Rollback) گردند.
یکی از روش های معروف برای اعمال Transaction، استفاده از کلاس TranasctionScope می باشد که به صورت توزیع شده نیز عمل می کند. یعنی حتی می توان تغییرات انجام شده بر روی چند پایگاه داده متفاوت را بی اثر نمود.
استفاده از این کلاس بسیار ساده می باشد و کافیست که قطعه کد های خود را داخل بلاک TranasctionScope بگذاریم.
با فراخوانی متد Complete در انتهای تمامی عملیات، تغییرات اعمال می شوند و در غیر اینصورت تغییرات بی اثر می شوند. به یاد داشته باشید که کلاس TranasctionScope در پس زمینه خود از امکانات +COM استفاده می کند و در نتیجه ملاحظات +COM در اینجا نیز باید مد نظر قرار گیرد. ضمنا برای استفاده از این کلاس باید ابتدا ارجاع (Reference) اسمبلی System.Transaction را به برنامه اضافه کنید.
روش دیگر اعمال Transaction بر روی اتصال (Connection) پایگاه داده می باشد.
اگر دقت کرده باشید در قطعه کدهایی که تاکنون نوشته ایم هیچ گاه اتصال پایگاه داده را باز یا بسته نکرده ایم (به جز در هنگام استفاده از ExecuteXmlReader). اگر بخواهیم یک Transaction را بر روی اتصال پایگاه داده اعمال کنیم باید شکل زیر عمل کنیم.
کلاس Database دارای متدی به نام CreateConnection می باشد که یک شیء از نوع DbConnection ایجاد کرده و برمی گرداند. کافیست که Transaction خود را (که در اینجا از نوع DbTransaction می باشد) بر روی این اتصال اعمال کنیم. به قطعه کد زیر توجه کنید.
در این روش باز و بسته نمودن اتصال به عهده خود ما می باشد و بنابراین پس از انجام عملیات باید اتصال را ببندیم. این عمل را با استفاده از بلاک using انجام داده ایم. اغلب متدهایی که دستورات را بر روی پایگاه داده اجرا می کنند دارای Overload هایی می باشند که یک پارامتر از نوع DbTransaction دریافت می کنند. برای اعمال تراکنش باید شیء DbTransaction را به این متد ها بفرستیم. در قطعه کد بالا برای متد ExecuteNonQuery این کار را انجام داده ایم.
پس از انجام عملیات، تراکنش را برای اعمال تغییرات Commit نموده ایم. اگر خطایی هنگام انجام عملیات پیش آید، ادامه کار به بلاک catch منتقل شده و تراکنش Rollback می شود.
در این مقاله ما برای تبدیل نمودن داده های بازیابی شده از پایگاه داده به اشیائی از نوع Studentاز روش پیمایش IDataReader در یک حلقه استفاده کردیم. کتابخانه Enterprise Library دارای دو متد به نام های ExecuteSprocAccessor و ExecuteSqlAccessor می باشد که این کار را در درون خود انجام می دهند. ما در این مقاله این روش را بررسی نکردیم و شما در صورت علاقه مند بودن می توانید در مورد آن تحقیق کنید.
مدیر تالار های: