0

آموزش کتابخانه Microsoft Enterprise Library 5.0

 
hosseinb68
hosseinb68
کاربر طلایی1
تاریخ عضویت : بهمن 1389 
تعداد پست ها : 1269
محل سکونت : بوشهر

آموزش کتابخانه Microsoft Enterprise Library 5.0

مقدمه:

قصد دارم در سلسله مقالاتی به معرفی امکانات و ویژگی های آخرین نسخه از کتابخانه Microsoft Enterprise Library به شکل کاملا کاربردی و با زبانی ساده بپردازم. این مقاله اولین قسمت از این سلسله مقالات می باشد.

یکی از ارزشمندترین محصولات شرکت مایکروسافت برای توسعه گران پلتفرم Net. کتابخانه Enterprise Library می باشد که متاسفانه در بین توسعه گران کشور ما کمترین استفاده را داشته است. دلیل این عدم استقبال به عقیده بنده عدم آشنایی با امکانات و نحوه کار با این کتابخانه می باشد.

تا قبل از انتشار نسخه آخر این کتابخانه (نسخه 5.0) یکی از بزرگترین مشکلات برای فراگیری این کتابخانه، عدم وجود مستندات فنی و آموزشی در مورد آن بود که همواره مورد اعتراض و انتقاد برنامه نوسان در سرتاسر جهان بود. تا اینکه سرانجام تیم patterns & practices شرکت مایکروسافت که خالق این کتابخانه می باشد، در ارائه آخرین نسخه از کتابخانه این خلأ را هم رفع نموده و هم اکنون بهترین زمان برای یادگیری و استفاده از این محصول ارزشمند می باشد.

نسخه آخر کتابخانه Enterprise Library به واقع یک سر و گردن بالاتر از نسخه های پیشین هم از جهت طراحی و هم از کاربردی می باشد.

در این مقاله به معرفی کلی و اجمالی امکانات این کتابخانه می پردازیم و در مقالات آتی به بررسی تک تک بلاک ها با رویکرد کاربردی و جزئیات مورد نیاز خواهیم پرداخت.

شروع:

به طور کلی نیازمندی های یک پروژه را می توان به نیازمندی های عملیاتی (functional requirements) و نیازمندی های غیر عملیاتی (non functional requirements) تقسیم نمود.

  • نیازمندی های عملیاتی (functional requirements): به نیازمندی هایی گفته می شود که به شکل مستقیم از دل کسب و کار (Business) بیرون می آیند. به طور مثال اگر قصد نوشتن برنامه ای برای ثبت نام دانشجویان یک دانشکده را داشته باشیم، نیازهای ثبت نام، انتخاب واحد و حذف و اضافه و ... جزو نیازمندی های عملیاتی پروژه می باشند.
  • نیازهای غیر عملیاتی (non functional requirements): این نوع نیازمندی ها هم معمولا در پروژه های نرم افزاری وجود دارند و به طور مستقیم با نوع کسب و کار ارتباط ندارند. از جمله این نیاز مندی ها می توان به نوع پایگاه داده و نحوه انجام تراکنش با آن، استفاده از مکانیزم Caching برای بالا بردن راندمان، استفاده از مکانیزم ها امنیتی برای اعتبار سنجی و رمز نگاری اطلاعات، مدیریت و اداره نموده خطاهای به وجود آمده در هنگام کار با نرم افزار و ... نام برد.

با اینکه در هنگام تعریف پروژه همواره نیاز های عملیاتی به عنوان مهمترین اهداف مطرح می شوند ولی برای توسعه گران نرم افزار، نیاز های غیر عملیاتی یکی از بحث برانگیزترین و چالش برانگیز ترین مباحث بخصوص در پروژه هایی با مقیاس متوسط و بزرگ بوده است. بدون ایجاد بستری مناسب و اتخاذ سیاست های درست در مورد نیازمندی های غیر عملیاتی، نگهداری، دیباگ و تغییرات در سیستم بسیار مشکل و پر دردسر خواهد بود.

به گفته قول خانم Mary Shaw استاد علوم کامپیوتر دانشگاه Carnegie Mellon:

"Less than 10% of the code has to do with the ostensible purpose of the system; the rest deals with input-output, data validation, data structure maintenance, and other housekeeping."

هدف اصلی از تولید کتابخانه Enterprise Library پاسخگویی به اینگونه از نیازها در پروژه های نرم افزاری می باشد. با استفاده صحیح از این کتابخانه و ایجاد بستری مناسب در نرم افزار، می توانید با خیال راحت حواس خود را بر روی نیاز های عملیاتی پروژه متمرکز نمایید و این موضوع باعث به وجود آمدن نرم افزارهایی با کیفیت قابل قبول خواهد شد.

کتابخانه Enterprise Library از 9 بلاک (Application Block) تشکیل شده است که هر بلاک برای پاسخگویی به نیازهای بخصوصی به وجود آمده است. هر بلاک متشکل از کامپوننت هایی با قابلیت استفاده مجدد (Reusable) می باشد.

از این 9 بلاک، تعداد 7 بلاک به شکل کاربردی (Functional Blocks) در برنامه قابل استفاده هستند و 2 بلاک دیگر جزو کامپوننت هایی هستند که سایر 7 بلاک دیگر در درون خود از امکانات این دو بلاک به مقاصد گوناگون از جمله نمونه سازی اشیاء (Object Initialization) استفاده می کنند. اصطلاحا به این دو بلاک Wiring Blocks گفته می شود.

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

در قسمت زیر این بلاک های را ملاحظه می کنید.
 

  •  Wiring Blocks
    •  Unity Dependency Injection
    •  Policy Injection Application Block
  • Functional Blocks
    •  Data Access Application Block
    •  Logging Application Block
    •  Exception Handling Application Block
    • Caching Application Block
    •  Validation Application Block
    •  Security Application Block
    •  Cryptography Application Block


معرفی Wiring Blocks:

همانطور که قبلا ذکر شد، این بلاک ها توسط سایر بلاک های کاربردی مورد استفاده قرار می گیرند. البته در مواردی می توانند مورد استفاده برنامه نویسان نیز قرار گیرند که در آینده بدان اشاره خواهیم نمود. در قسمت زیر معرفی کوتاهی از این دو بلاک را ملاحظه می کنید.

  1.  Unity Dependency Injection: این بلاک به منظور پیاده سازی مکانیزم تزریق وابستگی (Dependency Injection) مورد استفاده قرار می گیرد. مکانیزم تزریق وابستگی به منظور کمتر نمودن هر چه بیشتر وابستگی بین قسمت های مختلف نرم افزار و بیشتر نمودن پیوستگی آن ها مورد استفاده قرار می گیرد. این بلاک بسیار انعطاف پذیر بوده و روش های مختلف تزریق از جمله constructors و Methods و Properties را حمایت می کند. مبحث تزریق وابستگی (Dependency Injection) بسیار وسیع و عمیق می باشد که شرح آن خارج از اهداف این مقاله می باشد و علاقه مندان می توانند برای درک بیشتر این مفاهیم به جستجو در اینترنت بپردازند. به هر حال ما در آینده از امکانات این کتابخانه نیز استفاده خواهیم نمود. پس فعلا ذهن خود را درگیر آن نکنید.
  2. Policy Injection Application Block: این بلاک به منظور سازگار نمودن این نسخه از کتابخانه با نسخه های قبلی و در نتیجه امکان بروز رسانی برنامه هایی که از نسخه های قبلی این کتابخانه استفاده نموده اند، در این نسخه از کتابخانه قرار داد شده است و در حقیقت بیشتر توسط بلاک Unity Dependency Injection مورد استفاده قرار می گیرد.

معرفی بلاک های کاربردی (Functional Blocks):

اکنون به معرفی اجمالی 7 بلاک کاربردی می پردازیم. هدف از این سلسله مقالات، در حقیقت آموزش نحوه استفاده از این 7 بلاک می باشد و ما در مقالات آینده با رویکردی کاربردی و همراه با مثال به آموزش تمامی بلاک ها خواهیم پرداخت. ولی در این مقاله نگاهی کوتاه به عملکرد این 7 بلاک خواهیم داشت.

1. بلاک دیتا (Data Access Application Block):

این بلاک سال ها به عنوان پرکاربردترین و محبوب ترین بلاک از کتابخانه Enterprise Library مورد استفاده توسعه گران قرار گرفته است. با استفاده از این بلاک انجام اعمال متداول از قبیل Create, Delete, Update, Insert بر روی پایگاه داده به ساده ترین شکل قابل انجام می باشد.

موارد زیر جزو ویژگی های اصلی این کتابخانه می باشند:

  • با استفاده از این کتابخانه برنامه نویسان وابستگی کد خود را به نوع پایگاه داده به حداقل می رسانند و فقط با یک نوع پیاده سازی می توانند کد خود را برای پایگاه های داده مشهوری از قبیل Oracle, SQL Server, SQL Server CE و غیره مورد استفاده قرار داد.
  • یکی از بهترین ویژگی های این کتابخانه مدیریت اتصالات (باز و بسته نمودن و به اشتراک گذاشتن اتصالات) پایگاه داده می باشد که این موضوع به شکل داخلی در کتابخانه انجام شده و برنامه نویس را بی نیاز از نگرانی در این موارد می کند.

2. بلاک لاگ(Logging Application Block):

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

بلاک لاگ بسیار انعطاف پذیر بوده و می توان از آن به منظور لاگ نمودن اطلاعات مختلف در رسانه های متفاوت (XML، ایمیل، پایگاه داده و ...) استفاده نمود.

3. بلاک خطا و استثناء (Exception Handling Application):

اداره نمودن خطاها و استثناء ها یکی از مهمترین اعمال در پروژه های نرم افزاری می باشد. با توجه به گوناگون بودن خطاها (خطاهای امنیتی، خطاهای مروبط به پایگاه داده، خطاهای مربوط به عملکرد قطعه کدهای برنامه و ...) وجود یک سیستم قدرتمند برای اداره نمودن استثناء ها بسیار ضروری می باشد.

عملکرد این لایه مبتنی بر سیاست ها (Policy) می باشد که می توان برای استفاده هر گروه از دست اندر کاران سیستم (برنامه نویسان، مدیران، عیب یابان، کاربران و ...)  سیاست خاصی تعریف نمود و به ساده ترین شکل ممکن سیاست ها را تغییر داد (در مقالات بعدی این موضوع را مشاهده خواهید نمود).

4. بلاک کش (Caching Application Block):

همانطور که از نام این بلاک پیداست، به منظور ایجاد بستری مناسب برای استفاده از تکنولوژی Caching مورد استفاده قرار می گیرد.

5. بلاک اعتبار سنجی (Validation Application Block):

اعتبار سنجی داده های کاربران یکی از مهمترین وظایف در برنامه ها بوده که اتفاقا جزو زمانبر ترین وظایف نیز می باشد. این بلاک به منظور ارائه راه حل هایی برای اعتبار سنجی داده ها در محیط های مختلف (ویندوز، وب، سرویس و ...)  به وجود آمده است.

6. بلاک امنیت Security Application Block):

مکانیزم احراز هویت و تشخیص مجوز (Authentication & Authorization) مبتنی بر نقش ها (Rules) یکی از نیازهای مهم و متداول در برنامه ها می باشد. بلاک امنیت پیاده سازی هایی در مورد Active Directory Service &  Authorization Manager & Active Directory Application Mode داشته و همچنین امکان نوشتن پروایدر هایسفارشی را نیز دارد.

7. بلاک رمزنگاری (Cryptography Application Block):

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

وابستگی بلاک های کاربردی مختلف به یکدیگر :

همانگونه که در ابتدای مقاله ذکر شد، بلاک های کاربردی کتابخانه  Enterprise Library به طور پیشفرض به شکل مستقل قابل استفاده می باشند. ولی برای استفاده از برخی امکانات بلاک ها وابسته به بلاک های دیگر است.

به طور مثال اگر قرار باشد که اطلاعات بلاک لاگ در پایگاه داده ذخیره شود، ما نیاز داریم که از بلاک دیتا نیز استفاده کنیم. و یا اگر قرار باشد محتوای خطای به وجود آمده در برنامه که توسط بلاک استثناء اداره شده است را در محلی لاگ کنیم، نیاز داریم که از بلاک لاگ استفاده کنیم و اگر محل ذخیره سازی پایگاه داده باشد پس نیاز است از هر سه بلاک دیتا، لاگ و استثناء در کنار یکدیگر استفاده نمایید.

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

نام بلاک وابستگی ها شرایط
بلاک لاگ بلاک دیتا در صورتی که نیاز باش اطلاعات لاگ شده در پایگاه داده ذخیره شوند.
بلاک استثناء بلاک لاگ اگر خطای به وجود آمده نیاز به لاگ شدن داشته باشد
  بلاک دیتا اگر محل ذخیره سازی خطای به وجود آمده پایگاه داده باشد.
بلاک کش بلاک دیتا در صورتی که بخواهیم اطلاعات کش شده را در پایگاه داده ذخیره کنیم
  بلاک رمز نگاری اگر قرار باشد اطلاعات رمزنگاری شوند.
بلاک امنیت بلاک دیتا در صورتی که بخواهیم اطلاعات امنیتی کش شده را در پایگاه داده نگهداری کنیم.


نمایش گرافیکی جدول فوق را در قسمت زیر ملاحظه می کنید.
 


در قسمت بعدی از مقالات به معرفی پیش نیاز های نصب این کتابخانه و روش نصب آن خواهیم پرداخت.

پنج شنبه 16 آذر 1391  2:49 PM
تشکرات از این پست
babak110 ravabet_rasekhoon h10101010
hosseinb68
hosseinb68
کاربر طلایی1
تاریخ عضویت : بهمن 1389 
تعداد پست ها : 1269
محل سکونت : بوشهر

آموزش کتابخانه Microsoft Enterprise Library 5.0 - قسمت دوم

تذکر:

در قسمت قبلی به معرفی اجمالی کتابخانه Microsoft Enterprise Library 5.0 پرداختیم. توصیه می شود قبل از خواندن این مقاله، قسمت قبلی را مطالعه نمایید.

حداقل نیازهای سخت افزاری و نرم افزاری لازم جهت نصب برنامه Enterprise Library 5.0 را در قسمت زیر ملاحظه می کنید.

  • Supported Architectures: x86 and x64
  •  Operating System:
    •  Microsoft Windows® 7 Professional, Enterprise or Ultimate
    •  Windows Server 2003 R2
    •  Windows Server 2008 with Service Pack 2
    •  Windows Server 2008 R2.
    •  Windows Vista with Service Pack 2
  • Microsoft .NET Framework 3.5 with Service Pack 1 or Microsoft .NET Framework 4.0
  • Recommended Development Environment: Any of the following development systems:
    • Microsoft Visual Studio® 2008 Development System with Service Pack 1 (any edition)
    • Microsoft Visual Studio® 2010 Development System (any edition)
  • Unit Testing Requirements: To run the unit tests provided as part of the Enterprise Library source code installation we require the following:
    • Microsoft Visual Studio 2008 Professional or Visual Studio 2008 Team Edition or Visual Studio 2010 Premium or Visual Studio 2010 Professional, or Visual Studio 2010 Ultimate edition
    • Moq v3.1 assemblies, which can be downloaded from http://code.google.com/p/moq/

نصب Enterprise Library:

خوشبختانه کتابخانه Enterprise Library به صورت متن باز (Open Source) ارائه شده است. این کتابخانه را می توانید از هم طریق سایت MSDN و هم از طریق صفحه خانگی این پروژه در وب سایت CodePlex دریافت کنید.

در صورتی که قصد دارید از طریق سایت MSDN این برنامه را دریافت کنید، نسخه کامل این برنامه را که به شکل یک فایل اجرایی می باشد را دریافت کنید.
 


فایل برنامه را اجرا کنید تا پنچره نصب نمایان گردد.
 


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


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

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


 

در پنجره بالا از شما پرسیده است که آیا علاقه مند هستید که سورس برنامه را نیز داشته باشید و گزینه انتخاب به طور پیشفرض انتخاب شده می باشد. شما نیز گزینه پیشفرض را تغییر نداده و دکمه Finish را کلیک کنید.

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



پس از انتخاب محل مورد علاقه خود، دکمه Next را کلیک کنید تا برنامه سورس فایل ها را استخراج نموده و در محل مورد نظر شما کپی کند.

پس از انجام این کار آخرین پنجره را ملاحظه می کنید که از شما پرسیده است که آیا علاقه دارید فایل های سورس کپی شده را در همین لحظه کامپایل نماید! این گزینه به صورت پیشفرض انتخاب شده می باشد و در صورتی که دکمه Finish را کلیک کنید، پنجره ی کامپایل باز شده و پوشه ای به نام Bin در کنار سایر پوشه ها در محل نصب سورس برنامه ایجاد شده و اسمبلی های کامپایل شده در آن قرار می گیرد.
 





خوب کار تمام است و اکنون نوبت به استفاده از امکانات برنامه فرا رسیده است. در قسمت بعدی به معرفی کامل بلاک دیتا یعنی Data Application Block می پردازیم.

پنج شنبه 16 آذر 1391  2:51 PM
تشکرات از این پست
hosseinb68
hosseinb68
کاربر طلایی1
تاریخ عضویت : بهمن 1389 
تعداد پست ها : 1269
محل سکونت : بوشهر

آموزش کتابخانه Microsoft Enterprise Library 5.0 - قسمت سوم

تذکر:

در قسمت های قبلی به معرفی اجمالی کتابخانه Microsoft Enterprise Library 5.0 و نحوه نصب آن پرداختیم. توصیه می شود در صورت نیاز قبل از خواندن این مقاله، قسمت های قبلی را مطالعه کنید.

مقدمه:

بلاک دیتا (Data Application Block) معروف ترین و پر استفاده ترین بلاک در مجموعه Enterprise Library می باشد. میلیون ها نفر از برنامه نویسان و توسعه گران نرم افزار دنیا تاکنون از امکانات این بلاک استفاده نموده اند و امتحان خود را به بهترین شکل ممکن پس داده است.

امروزه با ظهور ابزار های ORM از قبیل LINQ to SQL، LINQ to Entity، NHibernate، OpenAccess ORM و غیره ساخت لایه دیتا و انجام اعمال متداول بر روی بانک های اطلاعاتی سهولت بسیار زیادی پیدا کرده است. این ابزار ها باعث صرفه جویی زیادی در وقت و زمان برنامه نویسان شده اند. البته هر کدام از این ابزار ها علاوه بر مزایای عمده ای که دارند، دارای نقاط ضعفی نیز می باشند.

صرف نظر از نقاط قوت و ضعف ابزارهای ORM، هر گاه نوبت به راندمان و کارایی می رسد، همچنان ADO.NET و Stored Procedure با اختلاف زیادی حرف اول را می زنند و بلاک دیتا ابزاری ایده آل برای کار با این عناصر می باشد.

تذکر:

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

  • در این مقالات برای سهولت و درک بهتر از واژه "پروسیجر" برای عبارت "Stored Procedure" استفاده می شود.

موارد زیر جزو برجسته ترین ویژگی های بلاک دیتا می باشد:

  • اعمال متداول بر روی پایگاه داده با تعداد خط های کد بسیار کمتری نسبت به ADO.NET انجام می شود. البته به یاد داشته باشید که خود این بلاک نیز در درون خود از ADO.NET استفاده می کند.

  • تمام اعمال حساسی از قبیل ایجاد اتصال پایگاه داده، باز کردن و بستن اشیاء اتصال (Connection Objects) ، کش نمودن پارامتر ها (Parameter Caching) و غیره توسط خود بلاک و به شکل درونی انجام شده و برنامه نویس را کاملا بی نیاز از نگرانی در مورد اینگونه جوانب نموده است.

  • با استفاده از این بلاک می توانیم برای انواع پایگاه های داده روز دنیا از قبیل Microsoft SQL Server، Oracle، SQL Server Compact Edition و ... برنامه نویسی کنیم. تغییر از یک نوع پایگاه داده به نوع دیگر با کمترین تغییرات قابل انجام می باشد.

  • پایگاه داده SQL Server Compact Edition قابلیت Connection Pooling را حمایت نمی کند و به همین دلیل انجام عمل باز نمودن و بستن اشیاء اتصال این پایگاه داده بسیار پرهزینه می باشد. خوشبختانه بلاک دیتا در درون خود مکانیزمی را برای انجام اعمال Connection Pooling و Parameter Caching در این پایگاه داده پیاده سازی نموده که باعث بالا رفتن راندمان و کارایی شده است.

در حال حاضر بلاک دیتا در آخرین نسخه انتشار خود پایگاه های داده Microsoft SQL Server و SQL Server Compact Edition و پایگاه داده Oracle 9i و نسخه های جدیدتر را حمایت می کند. خوشبختانه همانطور که در مقالات قبلی نیز اشاره شد، کتابخانه Enterprise Library به شکل متن باز ارائه شده است و جامعه برنامه نویسان متن باز، پیاده سازی های مختلفی را جهت کار با پایگاه های داده مختلفی از قبیل IBM DB2, MySQL, Oracle (ODP.NET), PostgreSQL, SQLite نوشته اند که با مراجعه به آدرس http://entlibcontrib.codeplex.com می توانید اطلاعات بیشتر را کسب کنید.

تذکر:

پیاده سازی قطعه کدهای مربوط به پایگاه داده Oracle به طور پیشفرض با توابع اسمبلی System.Data.OracleClient.dll انجام شده است. از نسخه 4.0، تیم توسعه دهنده این کتابخانه توصیه نموده اند که از به منظور استفاده از بلاک دیتا برای پایگاه داده Oracle از این پیاده سازی استفاده نکنید و به جای آن از پیاده سازی های دیگر مانند آنچه که در آدرس بالا موجود است (Oracle ODP.NET) استفاده کنید.

پیکربندی و تنظیمات بلاک های Enterprise Library:

برای استفاده از هر یک از بلاک های کتابخانه Enterprise Library نیاز به انجام مقداری تنظیمات و پیکربندی های مختلف داریم. برخی از این تنظیمات از طریق فایل پیکربندی (معمولا web.config و app.config) انجام می شود و برخی از تنظیمات از طریق کد و برنامه نویسی. برخی تنظیمات را نیز می توان از هر دو طریق انجام داد.

به طور کلی اضافه نمودن امکانات بلاک های مختلف به یک برنامه شامل چند مرحله می باشد:

  1. انجام پیکربندی و اعمال تنظیمات در فایل پیکربندی برنامه

  2. اضافه نمودن اسمبلی های مورد نیاز به برنامه (با توجه به بلاک های انتخاب شده)

  3. اضافه نمودن فضاهای نامی (name space) مروبطه به برنامه

قبل از اینکه وارد جزئیات پیکربندی شویم، به محتویات فایل پیکربندی بلاک دیتا در یک فایل app.config توجه کنید.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <configSections>
    <section name="dataConfiguration"
             type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings,
             Microsoft.Practices.EnterpriseLibrary.Data, Version=5.0.414.0, Culture=neutral,
             PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
  </configSections>

  <dataConfiguration defaultDatabase="EntLibBook-DataAccess" />

  <connectionStrings>
    <add name="EntLibBook-DataAccess" connectionString="server=.\SQLEXPRESS;
         database=EntLibBook-DataAccess;Integrated Security=true;"
        providerName="System.Data.SqlClient" />
  </connectionStrings>

</configuration>

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

 هر کدام از بلاک های کتابخانه Enterprise Library نیاز به تعریف یک section در قسمت configSections دارند. در فایل پیکربندی بالا ما یک section به نام  dataConfiguration تعریف کرده ایم که قرار است تنظیمات مربوط به بلاک دیتا را نگهداری کند. سپس اسمبلی مورد استفاده برای تنظیمات بلاک دیتا و ورژن مربوطه و سایر مشخصات را ذکر کرده ایم.

خوشبختانه بلاک دیتا تنظیمات بسیار کمی دارد و در فایل بالا در قسمت dataConfiguration فقط نام رشته اتصال پیشفرض پایگاه داده یعنی defaultDatabase را مشخص کرده ایم. با استفاده از بلاک دیتا می توان همزمان با پایگاه های داده مختلفی ارتباط برقرار نمود. به طور مثال اگر در قسمت رشته های اتصال پایگاه داده یعنی المنت connectionStrings چندین رشته اتصال داشته باشیم، هنگام کار با بلاک دیا می توانیم از هر کدام از آن ها استفاده کنیم. ولی یک رشته اتصال را باید به عنوان پیشفرض انتخاب کنیم تا مجبور نباشیم هنگام کار با بلاک دیتا مرتبا نام رشته اتصال مربوطه را ذکر کنیم. در ادامه مقاله هنگام ذکر مثال های کاربردی با این موضوع بیشتر آشنا خواهید شد.

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

خوب همانطور که ملاحظه می کنید اعمال تنظیمات بلاک ها به شکل مستقیم در فایل پیکربندی بسیار پر دردسر و تقریبا غیر ممکن می باشد ولی گاهی نیاز می شود که برخی از تغییرات جزئی را به شکل مستقیم در فایل پیکربندی اعمال کنیم. به همین دلیل اعضای تیم تولید کننده Enterprise Library یک برنامه کنسول برای نسخه های DotNet 3.5 و DotNet 4.0 و برای سیستم های 32 و 64 بیتی فراهم نموده اند تا بتوان با استفاده از آن براحتی تنظیمات مربوط به بلاک های مختلف را بر روی فایل پیکربندی اعمال نمود.



 

قبل از اینکه به سراغ کنسول پیکربندی بلاک ها برویم باید بدانید که فایل های پیکربندی (معمولا web.config یا app.config) به طور پیشفرض اسکیمای تگ های Enterprise Library را نمی شناسند و در نتیجه هنگام تغییرات در آن ها خبری از قابلیت IntelliSense نمی باشد. برای اینکه قابلیت IntelliSense را برای تگ های Enterprise Library فراهم کنید، کافیست مراحل زیر را دنبال کنید.

1. فایل پیکربندی را در ویژوال استودیو باز کنید.

2. از منوی XML گزینه ...Schemas را انتخاب کنید.



 

3. از پنجره باز شده، ردیف مربوط به اسکیمای Enterprise Library را پیدا کرده و گزینه Use this schema را انتخاب نموده و پنجره را ببندید.



 

اکنون اگر به فایل پیکربندی مراجعه کنید، متوجه خواهید شد که قابلیت IntelliSense برای تگ های Enterprise Library فعال شده است.

کار با کنسول پیکربندی:

ایجاد و ویرایش تنظیمات توسط کنسول پیکربندی از دو طریق امکان پذیر می باشد. در صورتی که بخواهید پنجره کنسول را به شکل مستقل اجرا کنید، کافیست که پوشه نصب برنامه، کنسول مناسب خود را (دات نت 3.5 یا 4.0 و نسخه 32 یا 64 بیتی) اجرا نمایید. در این صورت پنجره کنسول برای شما باز می شود و از طریق آن می توانید که فایل پیکربندی جدید ایجاد کنید و یا فایلی را که قبلا ایجاد شده است را ویرایش نمایید.

راه دوم از طریق ویژوال استودیو می باشد. شما می توانید برای اعمال تنظیمات بلاک ها بر روی فایل پیکربندی کلیک راست نموده و گزینه Edit Enterprise Library V5 Configuration را انتخاب کنید.

 


تذکر بسیار مهم:

در صورت نیاز قبل از اعمال تغییرات بر روی فایل پیکربندی حتما یک نسخه پشتیبان از آن تهیه کنید تا در صورت خرابکاری بتوانید به نسخه قبلی خود دسترسی داشته باشید.

پنجره فایل پیکربندی مشابه شکل زیر می باشد.

 

از منوی Blocks می توانید بلاک های مختلف را به برنامه اضافه نموده و تنظیمات آن ها را انجام دهید. با صرف مقداری زمان و کار کردن با این کنسول می توانید به اکثر ویژگی های آن مسلط شوید. ما در ادامه این سلسله مقالات اکثر ویژگی ها را معرفی خواهیم نمود.

از منوی Blocks گزینه Add Data Settings را انتخاب نموده و کمی با ویژگی های مختلف آن کار کنید. به طور مثال ما در شکل زیر بلاک دیتا را اضافه نموده ایم و خاصیت Default Database Instance را به ConnectionString1 تغییر داده ایم.


 

تنظیمات را ذخیره نموده و برای مشاهد تغییرات به فایل پیکربندی مراجعه کنید.

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

پنج شنبه 16 آذر 1391  2:58 PM
تشکرات از این پست
hosseinb68
hosseinb68
کاربر طلایی1
تاریخ عضویت : بهمن 1389 
تعداد پست ها : 1269
محل سکونت : بوشهر

آموزش کتابخانه Microsoft Enterprise Library 5.0 - قسمت چهارم

مقدمه:

برای استفاده از امکانات بلاک دیتا (Data Application Block) باید 4 اسمبلی زیر را به برنامه خود اضافه کنید. البته تحت شرایطی ممکن است نیاز به اضافه نمودن اسمبلی های دیگری نیز باشد که در آینده ملاحظه خواهید نمود.
 


تذکر بسیار مهم:

اسمبلی های مربوطه را باید از دایرکتوری نصب برنامه کپی کنید (یا ارجاع دهید). به طور مثال برای بنده این اسمبلی ها در مسیر زیر ذخیره شده اند.

C:\Program Files\Microsoft Enterprise Library 5.0\Bin

هنگام کار با ابزار پیکربندی این کتابخانه، خصوصیاتی از قبیل PublicKeyToken و Version و ... تولید می شود که مختص اسمبلی هایی می باشد که به طور پیشفرض در این نسخه از کتابخانه تعبیه شده اند و هنگام نصب در محل دایرکتوری برنامه کپی می شوند.

<section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings,
          Microsoft.Practices.EnterpriseLibrary.Data,
          Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />

اگر به خاطر بیاورید در مقالات قبلی هنگام شرح نحوه نصب این کتابخانه، دیدیم که در انتهای مراحل نصب، یک ورژن دیگر از این اسمبلی ها در همان زمان کامپایل شده و کنار محل ذخیره سازی سورس برنامه، ذخیره شدند. در صورتی که بخواهیم از این اسمبلی ها استفاده کنیم باید خصوصیت PublicKeyToken را تغییر داده و یا حذف نماییم. این مسائل مربوط به Strong Name بودن اسمبلی ها می شود و ما از بحث بیشتر در مورد آن خودداری می کنیم.

از میان 5 اسمبلی ذکر شده در قسمت بالا، چهار تای اول برای استفاده از تمامی بلاک ها الزامی می باشند ولی اسمبلی پنجم مختص بلاک دیتا می باشد.

برای استفاده از امکانات بلاک دیتا باید فضاهای نامی زیر را به برنامه اضافه کنید.

  • Microsoft.Practices.EnterpriseLibrary.Data
  • Microsoft.Practices.EnterpriseLibrary.Common.Configuration
  • System.Data
  • System.Data.Common

اگر بخواهید از مکانیزم تزریق وابستگی (Dependency Injection) که توسط بلاک Unity ایجاد شده است، استفاده نمایید باید فضاهای نامی زیر را نیز به برنامه اضافه کنید (ما در این سلسله مقالات از این روش استفاده نخواهیم کرد).

  • System.Configuration
  • Microsoft.Practices.Unity
  • Microsoft.Practices.Unity.Configuration

نحوه نمونه سازی اشیاء (Object Instantiation) در کتابخانه Enterprise Library 5:

در نسخه های قبلی این کتابخانه، کلاس هایی به شکل Factory وجود داشتند که وظیفه نمونه سازی اشیاء را به عهده می گرفتند. به طور مثال کلاسی به نام DatabaseFactory وجود داشت (و البته هنوز هم وجود دارد!) که حاوی متدی به نام  CreateDatabse بود. این متد یک نمونه شیء از کلاس Database را برمی گرداند که با استفاده این کلاس تمام اعمال مربوط به پایگاه داده انجام می شود. در قسمت زیر نحوه نمونه سازی کلاس Database توسط کلاس DatabaseFactory را ملاحظه می کنید.

Database db = DatabaseFactory.CreateDatabase();

پس از ارائه آخرین نسخه، استفاده از این روش منسوخ شده است. البته به منظور سازگار بودن با نسخه های پیشین همچنان این امکان وجود دارد که از این روش استفاده کنید ولی این عمل توصیه نمی شود و ما نیز از این روش استفاده نخواهیم نمود.

دو روش برای نمونه سازی اشیاء در کتابخانه Enterprise Library 5 توصیه شده است.

  1. استفاده از Unity Service Locator (روش ساده تر)

  2. استفاده از Unity Container (روش پیچیده تر)

نمونه سازی اشیاء به روش Unity Service Locator:

به نحوه نمونه سازی کلاس Database با استفاده از روش Unity Service Locator دقت کنید.

Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>();

نمونه سازی اشیاء در کلیه بلاک ها با استفاده از کلاس EnterpriseLibraryContainer انجام می شود. این کلاس دارای یک پراپرتی استاتیک به نام Current می باشد که این پراپرتی یک آبجکت از نوع IServiceLocator را برمی گرداند. اینترفیس IServiceLocator دارای یک متد جنریک به نام GetInstance می باشد که نام کلاس مورد تقاضای ما را (در اینجا Database) را دریافت نموده و عمل نمونه سازی از آن را انجام می دهد.

متد GetInstance دارای یک Overload دیگر نیز می باشد که نام رشته اتصال (Connection String) خاصی را که علاقه مند هستیم از آن استفاده کنیم را دریافت می کند. در قطع کد بالا با توجه به اینکه ما نام رشته اتصال پایگاه داده را به این متد نفرستاده ایم پس نمونه سازی با استفاده از رشته اتصال پیشفرض (که در فایل پیکربندی مشخص شده است) انجام می شود. ولی در صورت تمایل می توانید رشته اتصال مورد نظر خود را صریحا اعلام کنید تا نمونه سازی بر اساس آن انجام شود.

Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>("ConnectionString2");

نمونه سازی اشیاء به روش Unity Container:

قبل از اینکه به معرفی اجمالی این روش بپردازیم باید بدانید که در حقیقت در روش Unity Service Locator نیز از Unity Container برای نمونه سازی اشیاء استفاده می شود ولی برای اینکه برنامه نویس به طور مستقیم درگیر کار با Unity Container نگردد، این عمل به شکل داخلی و پنهان انجام می شود و بنابراین شما فقط کافیست که فقط متد GetInstance را فراخوانی کنید.

در روش نمونه سازی اشیاء با استفاده از Unity Container شما امکان پیدا می کنید که به طور مستقیم با Unity Container که یک Dependency Injection Container می باشد، کار کنید. همانطور که قبلا هم بار ها ذکر کرده ایم، مبحث مربوط به تزریق وابستگی (Dependency Injection) خارج از اهداف این مقاله می باشد و ما قصد ورود به آن را نداریم. با استفاده از Unity Container شما می توانید حتی کلاس های داخلی برنامه خود را با استفاده از مکانیزم تزریق وابستگی نمونه سازی کنید که البته این مسئله نیاز به انجام اعمال بیشتری نیز دارد.

نمونه سازی اشیاء با استفاده از این روش به شکل زیر انجام می پذیرد.

var container = new UnityContainer();
container.AddNewExtension<EnterpriseLibraryCoreExtension>();
Database db3 = container.Resolve<Database>();

ما در این سلسله مقالات از روش 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 با ساختار زیر در پایگاه داده داریم که دارای تعدادی رکورد می باشد و قصد داریم اعمال متداول را بر روی آن انجام دهید.

CREATE TABLE [Student](
[StudentId] [int] IDENTITY(1,1) NOT NULL,
[FirstName] [nvarchar](100) NOT NULL,
[LastName] [nvarchar](100) NOT NULL,
[Age] [int] NOT NULL,
)

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

public class Student
{
    public int StudentId { get; set; }

    public string FirstName { get; set; }

    public string LirstName { get; set; }

    public int Age { get; set; }
}

انجام اعمال مختلف بر روی پایگاه داده معمولا از دو طریق انجام می پذیرد:

  1. Stored Procedure: روال های ذخیره شده در پایگاه داده می باشند که ما در این مقاله آن را پروسیجر خطاب می کنیم (مانند uspInsertStudent).
  2. SQL Comment: یک رشته دستور قابل فهم برای پایگاه داده می باشد (مانند Select * from Student).

Overload های توابع کلاس Database به طور کلی به دو دسته تقسیم می شوند. دسته اول Overloadمی باشند که یک پارامتر های آن ها از نوع DbCommand می باشد و به وسیله این متدها همه نوع اعمالی را می توان انجام داد. دسته دوم  Overload هایی هستند که نام یک پروسیجر یا یک دستور SQL را به همراه مجموعه ای از مقادیر (به عنوان مقادیر پارامتر ها) گرفته و عملیات را روی پایگاه داده انجام می دهند. Overload های نوع اول انعطاف پذیر تر هستند در حالیکه Overload های نوع دوم مقداری محدودیت دارند ولی در عوض استفاده از آن ها ساده تر می باشد. در ادامه مقاله و هنگام طرح مثال ها با این دو گروه بیشتر آشنا خواهید شد.

تذکر:

قبل از طرح مثال ها تاکید می شود که سورس کامل این مقاله از طریق لینک بالای صفحه قابل دریافت می باشد.

مثال اول: بازیابی اطلاعات دانش آموزی که شناسه او 1 بوده و سن او 20 سال می باشد

پروسیجر:

CREATE PROCEDURE uspGetStudent
(
@studentId INT,
@age INT
)
AS
SELECT * FROM Student WHERE StudentId = @studentId AND Age = @age

نوشتن متدی برای انجام این کار با استفاده از پروسیجر:

private Student GetStudenByUsingProcedure(int studentId, int age)
{
    Student student = null;
    string procedureName = "uspGetStudent";

    object[] values = new object[] { studentId, age };

    Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>();
    using (IDataReader reader = db.ExecuteReader(procedureName, values))
    {
        if (reader.Read())
        {
            student = new Student();
            student.StudentId = (int)reader["StudentId"];
            student.FirstName = reader["FirstName"].ToString();
            student.LastName = reader["LastName"].ToString();
            student.Age = (int)reader["Age"];
        }
    }
    return student;
}

در روش متد بالا، مقادیری که قرار است برای پروسیجر ارسال شود را داخل آرایه ای از اشیاء به نام 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:

private Student GetStudenByUsingSQL(int studentId, int age)
{
    Student student = null;
    string sql = "SELECT * FROM Student WHERE StudentId=@studentId AND Age=@age";

    Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>();

    DbCommand command = db.GetSqlStringCommand(sql);
    db.AddInParameter(command, "@studentId", DbType.Int32, studentId);
    db.AddInParameter(command, "@age", DbType.Int32, age);

    using (IDataReader reader = db.ExecuteReader(command))
    {
        if (reader.Read())
        {
            student = new Student();
            student.StudentId = (int)reader["StudentId"];
            student.FirstName = reader["FirstName"].ToString();
            student.LastName = reader["LastName"].ToString();
            student.Age = (int)reader["Age"];
        }
    }
    return student;
}

در متد بالا ما از یک روش دیگر که همان استفاده از DbCommand می باشد، استفاده کرده ایم. ابتدا دستور sql را به متد GetSqlStringCommand ارسال کرده ایم تا شیء DbCommand مناسب برای دستور SQL ساخته شود. سپس با استفاده از متد AddInParameter مقادیر را به پارامتر ها نسبت داده ایم و در نهایت شیء DbCommand را برای اجرای دستور به متد ExecuteReader ارسال کرده ایم.

مثال دوم: بازیابی مشخصات تمام دانشجویانی که سن آن 20 سال می باشد:

پروسیجر:

CREATE PROCEDURE uspGetStudentsByAge
(
@age INT
)
AS
SELECT * FROM Student WHERE Age=@age

نوشتن متدی برای انجام این کار با استفاده از پروسیجر:

private List<Student> GetStudentsByAgeUsingProcedure(int age)
{
    Student student = null;
    List<Student> students = new List<Student>();
    string procedureName = "uspGetStudentsByAge";

    object[] values = new object[] { age };

    Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>();
    using (IDataReader reader = db.ExecuteReader(procedureName, values))
    {
        while (reader.Read())
        {
            student = new Student();
            student.StudentId = (int)reader["StudentId"];
            student.FirstName = reader["FirstName"].ToString();
            student.LastName = reader["LastName"].ToString();
            student.Age = (int)reader["Age"];

            students.Add(student);
        }
    }
    return students;
}

همانطور که ملاحظه می کنید، منطق کار تغییر چندانی نکرد و فقط ما به جای اینکه اطلاعات یک دانش آموز را بازیابی کنیم، اطلاعات تعدای از آن ها را بازیابی نموده ایم.

نوشتن متدی برای انجام  این کار با استفاده از دستورات SQL:

private List<Student> GetStudentsByAgeUsingSQL(int age)
{
    Student student = null;
    List<Student> students = new List<Student>();
    string sql = "SELECT * FROM Student WHERE Age=@age";

    Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>();

    DbCommand command = db.GetSqlStringCommand(sql);
    db.AddInParameter(command, "@age", DbType.Int32, age);

    using (IDataReader reader = db.ExecuteReader(command))
    {
        while (reader.Read())
        {
            student = new Student();
            student.StudentId = (int)reader["StudentId"];
            student.FirstName = reader["FirstName"].ToString();
            student.LastName = reader["LastName"].ToString();
            student.Age = (int)reader["Age"];
        }
    }

    return students;
}

در این متد هم تغییر چندانی ایجاد نکردیم.

مثال سوم: بازیابی لیست تمامی دانشجویان:

در مثال های قبل ما مقادیری را برای پروسیجر یا دستور SQL ارسال می کردیم. در این مثال هیج مقداری را ارسال نمی کنیم و فقط قصد داریم لیست تمامی دانشجویان را بازیابی کنیم.

پروسیجر:

CREATE PROCEDURE uspGetAllStudents
AS
SELECT * FROM Student

نوشتن متدی برای انجام این کار با استفاده از پروسیجر:

private List<Student> GetAllStudentsUsingProcedure()
{
    Student student = null;
    List<Student> students = new List<Student>();
    string procedureName = "uspGetAllStudents";

    Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>();
    using (IDataReader reader = db.ExecuteReader(CommandType.StoredProcedure, procedureName))
    {
        while (reader.Read())
        {
            student = new Student();
            student.StudentId = (int)reader["StudentId"];
            student.FirstName = reader["FirstName"].ToString();
            student.LastName = reader["LastName"].ToString();
            student.Age = (int)reader["Age"];

            students.Add(student);
        }
    }
    return students;
}

این بار از یک Overload متد ExecuteReader استفاده کرده ایم که CommandType.StoredProcedure را به عنوان پارامتر اول گرفته و نام پروسیجر را به عنوان پارامتر دوم دریافت می کند.

نوشتن متدی برای انجام  این کار با استفاده از دستورات SQL:

دقیقا مانند متد بالا می باشد با این تفاوت که این بار CommandType را از نوع CommandType.Text تعیین می کنیم.

private List<Student> GetAllStudentsUsingSQL()
{
    Student student = null;
    List<Student> students = new List<Student>();
    string sql = "SELECT * FROM Student";

    Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>();
    using (IDataReader reader = db.ExecuteReader(CommandType.Text, sql))
    {
        while (reader.Read())
        {
            student = new Student();
            student.StudentId = (int)reader["StudentId"];
            student.FirstName = reader["FirstName"].ToString();
            student.LastName = reader["LastName"].ToString();
            student.Age = (int)reader["Age"];

            students.Add(student);
        }
    }
    return students;
}

مثال چهارم: بازیابی سن یک دانشجو:

گاهی اوقات خروجی دستور SQL ما یک تک مقدار می باشد. هر چند از روش های گفته شده در بالا نیز می توان این عمل را انجام داد ولی با بهتر است که با عملکرد تابع ExecuteScalar نیز آشنا شوید.

پروسیجر:

CREATE PROCEDURE uspGetStudentAge
(
@studentId INT
)
AS
SELECT Age FROM Student WHERE StudentId=@studentId

نوشتن متدی برای انجام این کار با استفاده از پروسیجر:

private int GetStudenAgeUsingProcedure(int studentId)
{
    int age = -1;
    string procedureName = "uspGetStudentAge";

    object[] values = new object[] { studentId };

    Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>();
    object result = db.ExecuteScalar(procedureName, values);
    if (result != null)
    {
        age = Convert.ToInt32(result);
    }

    return age;
}

ابتدا مقدار age را به طور پیشفرض برابر 1- تعیین کرده ایم تا اگر اصلا چنین دانش آموزی وجود نداشت متوجه آن شویم. عملکرد سایر قسمت های کد کاملا شفاف می باشد.

نوشتن متدی برای انجام  این کار با استفاده از دستورات SQL:

private int GetStudenAgeUsingSQL(int studentId)
{
    int age = -1;
    string sql = "SELECT Age FROM Student WHERE StudentId=@studentId";

    Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>();
    DbCommand command = db.GetSqlStringCommand(sql);
    db.AddInParameter(command, "@studentId", DbType.Int32, studentId);

    object result = db.ExecuteScalar(command);
    if (result != null)
    {
        age = Convert.ToInt32(result);
    }

    return age;
}

عملکرد متد بالا نیز کاملا شفاف می باشد.

مثال پنجم: بازیابی اطلاعات دانشجویان به شکل یک دیتاست(DataSet):

تاکنون تمامی اطلاعات را با استفاده از IDataReader بازیابی نموده ایم. اکنون قصد داریم اطلاعات را به صورت یک دیتاست بازیابی کنیم. برای انجام این کار در بلاک دیتا از دو متد ExecuteDataSet و LoadDataSet استفاده می شود. عملکرد این دو متد یکسان می باشد با این تفاوت که در متد ExecuteDataSet یک شیء دیتاست جدید ایجاد شده و برگردانده می شود در حالیکه در متد LoadDataSet بر روی دیتاستی که در حال حاضر موجود است (باید به عنوانی یک پارامتر برای این متد بفرستیم) تغییرات اعمال می شود.

CREATE PROCEDURE uspGetAllStudents
AS
SELECT * FROM Student

نوشتن متدی برای انجام این کار با استفاده از پروسیجر:

private DataSet GetAllStudentsDatasetUsingProcedure()
{
    Student student = null;
    string procedureName = "uspGetAllStudents";

    Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>();
    DataSet ds = db.ExecuteDataSet(CommandType.StoredProcedure, procedureName);

    return ds;
}

در قطعه کد بالا از متد ExecuteDataSet استفاده شده است.

نوشتن متدی برای انجام  این کار با استفاده از دستورات SQL:

private DataSet GetAllStudentsDatasetUsingSQL()
{
    Student student = null;
    string sql = "SELECT * FROM Student";

    Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>();
    DataSet ds = db.ExecuteDataSet(CommandType.Text, sql);

    return ds;
}

مثال ششم: بازیابی اطلاعات با فرمت XML:

برای بازیابی اطلاعات به شکل XML از متد ExecuteXmlReader استفاده می شود. این متد مخصوص پایگاه داده Microsoft SQL Server می باشد و بنابراین تعریف آن در کلاس SqlDatabase انجام شده است. برای استفاده از این متد باید کلاس Database را تبدیل به SqlDatabase کنیم. متدهای آسنکرون BeginExecuteXmlReader و EndExecuteXmlReader نیز در این کلاس تعریف شده اند که در صورت نیاز می توانید از آن ها استفاده کنید.

در قطعه کد زیر ما لیست مشخصات تمامی دانش آموزان را به شکل یک رشته XML بازیابی نموده ایم.

private string GetAllStudentsAsXML()
{
    Student student = null;
    string sql = "SELECT * FROM Student FOR XML AUTO";

    SqlDatabase db = EnterpriseLibraryContainer.Current.GetInstance<Database>() as SqlDatabase;
    DbCommand command = db.GetSqlStringCommand(sql);

    string result = "";
    try
    {
        using (XmlReader reader = db.ExecuteXmlReader(command))
        {
            // Read Data
            while (!reader.EOF)
            {
                if (reader.IsStartElement())
                {
                    result += reader.ReadOuterXml();
                }
            }
        }
    }
    finally
    {
        // Close Connection
        if (command.Connection != null)
        {
            command.Connection.Close();
        }
    }
    return result;
}

در دستور SQL قطعه کد بالا از عبارت FOR XML AUTO برای بازیابی شدن اطلاعات با فرمت XML استفاده کرده ایم و سپس برای دسترسی به این متد شیء Database را تبدیل به SqlDatabase کرده ایم. خروجی متد ExecuteXmlReader از نوع XmlReader می باشد که با پیمایش آن می توانیم اعمال مورد نظر خود را انجام دهیم.

تذکر: هنگام استفاده از متد ExecuteXmlReader حتما باید اتصال پایگاه داده (Connection) را صریحا ببندید. این متد پس از اجرا اتصال را نمی بندد و حتی XmlReader نیز هنگامی که Dispose می شود باز هم اتصال باز می ماند. ما در متد بالا در قسمت finally اتصال را بسته ایم.

محتویات متغیر result در قطع کد بالا مشابه زیر می باشد.

<Student StudentId="1" FirstName="Morteza" LastName="Sahragard" Age="20" />

<Student StudentId="2" FirstName="Ali" LastName="Rahmani" Age="20" />

خوب، روش های مهم بازیابی اطلاعات را بررسی کردیم. اکنون به سراغ وارد نمودن اطللاعات جدید می رویم.

مثال هفتم: وارد نمودن رکورد جدید در پایگاه داده (بدون مقدار برگشتی):

به طور معمول وارد نمودن اطلاعات در پایگاه داده یا با مقدار برگشتی (Output Value) می باشد و یا بدون آن. در این مثال بدون مقدار برگشتی را بررسی می کنیم.

پروسیجر:

CREATE PROCEDURE uspInsertUserWithoutReturnValue
(
@firstName nvarchar(100),
@lastName nvarchar(100),
@age int
)
AS
INSERT INTO dbo.Student( FirstName, LastName, Age )
VALUES ( @firstName,@lastName, @age )

نوشتن متدی برای انجام این کار با استفاده از پروسیجر:

private void InsertUserWithoutReturnValueUsingProcedure(string firstName, string lastName, int age)
{
    string procedureName = "uspInsertUserWithoutReturnValue";
    object[] values = new object[] { firstName, lastName, age };

    Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>();
    db.ExecuteNonQuery(procedureName, values);
}

ملاحظه می کنید که تنها تغییر این متد استفاده از ExecuteNonQuery می باشد و سایر قضایا همانند متدهای قبلی می باشد.

نوشتن متدی برای انجام  این کار با استفاده از دستورات SQL:

private void InsertUserWithoutReturnValueUsingSQL(string firstName, string lastName, int age)
{
    string sql = "INSERT INTO dbo.Student( FirstName, LastName, Age)VALUES  ( @firstName,@lastName, @age)";

    Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>();
    DbCommand command = db.GetSqlStringCommand(sql);
    db.AddInParameter(command, "@firstName", DbType.String, firstName);
    db.AddInParameter(command, "@lastName", DbType.String, lastName);
    db.AddInParameter(command, "@age", DbType.Int32, age);

    db.ExecuteNonQuery(command);
}

مثال هفتم: وارد نمودن رکورد جدید در پایگاه داده (همراه با مقدار برگشتی):

پروسیجر:

CREATE PROCEDURE uspInsertUserWithReturnValue
(
@firstName nvarchar(100),
@lastName nvarchar(100),
@age INT,
@studentId INT OUTPUT
)
AS
INSERT INTO dbo.Student( FirstName, LastName, Age )
VALUES ( @firstName,@lastName, @age )

SET @studentId=SCOPE_IDENTITY();
RETURN

نوشتن متدی برای انجام این کار با استفاده از پروسیجر:

private int InsertUserWithReturnValueUsingProcedure(string firstName, string lastName, int age)
{
    int studentId = -1;
    string procedureName = "uspInsertUserWithReturnValue";

    Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>();
    DbCommand command = db.GetStoredProcCommand(procedureName);
    db.AddInParameter(command, "@firstName", DbType.String, firstName);
    db.AddInParameter(command, "@lastName", DbType.String, lastName);
    db.AddInParameter(command, "@age", DbType.Int32, age);
    //add output parameter
    db.AddOutParameter(command, "@studentId", DbType.Int32, int.MaxValue);

    int numberOfAffectedRows = db.ExecuteNonQuery(command);
    if (numberOfAffectedRows > 0)
    {
        studentId = (int)db.GetParameterValue(command, "@studentId");
    }
    return studentId;
}

با توجه به اینکه ما نیاز به دریافت پارامتر خروجی پروسیجر داریم، از روش DbCommand استفاده کرده ایم. دقت کنید که برای ساخت DbCommand این بار از متد GetStoredProcCommand استفاده نموده ایم چون قصد داریم شیء DbCommand با استفاده از پروسیجر ساخته شود. پارامتر خروجی را با استفاده از متد AddOutParameter اضافه کرده ایم. متد ExecuteNonQuery همانند مشابه خود در ADO.NET معمولی، پس از اجرا دستور، تعداد رکوردهایی که مورد تاثیر اجرای دستور قرار گرفته اند را برمی گرداند. در نهایت با استفاده از متد GetParameterValue مقدار پارامتر خروجی را بازیابی نموده ایم.

نوشتن متدی برای انجام  این کار با استفاده از دستورات SQL:

private int InsertUserWithReturnValueUsingSQL(string firstName, string lastName, int age)
{
    int studentId = -1;
    string sql = "INSERT INTO dbo.Student( FirstName, LastName, Age )VALUES  ( @firstName,@lastName, @age  );" +
    "SET @studentId=SCOPE_IDENTITY();return;";

    Database db = EnterpriseLibraryContainer.Current.GetInstance<Database>();
    DbCommand command = db.GetSqlStringCommand(sql);
    db.AddInParameter(command, "@firstName", DbType.String, firstName);
    db.AddInParameter(command, "@lastName", DbType.String, lastName);
    db.AddInParameter(command, "@age", DbType.Int32, age);
    //add output parameter
    db.AddOutParameter(command, "@studentId", DbType.Int32, int.MaxValue);

    int numberOfAffectedRows = db.ExecuteNonQuery(command);
    if (numberOfAffectedRows > 0)
    {
        studentId = (int)db.GetParameterValue(command, "@studentId");
    }
    return studentId;
}

روند کار شبیه متد قبلی می باشد با این تفاوت که این بار به جای پروسیجر از دستور SQL استفاده نموده ایم و از متد GetSqlStringCommand جهت ساخت شیء DbCommand استفاده کردیم.

بروز رسانی و حذف اطلاعات:

بروز رسانی و حذف اطلاعات دقیقا همانند ورود اطلاعات جدید که در بالا بحث کردیم توسط متد ExecuteNonQuery انجام می شود با این تفاوت که محتوای دستور SQL یا پروسیجر از Delete یا Update استفاده می کند.

استفاده از Transaction:

در هنگام کار با پایگاه داده گاهی نیاز است از Transaction استفاده کنیم. این موضوع معمولا زمانی اتفاق می افتد که نیاز داریم چندین تغییر در پایگاه داده ایجاد کنیم و این تغییرات وابسته به یکدیگر می باشند. اگر انجام یکی از تغییرات با شکست روبرو شود (و یا به هر نحوی نتیجه مور نظر ما حاصل نگردد) باید تمامی تغییرات بی اثر (Rollback) گردند.

یکی از روش های معروف برای اعمال Transaction، استفاده از کلاس TranasctionScope می باشد که به صورت توزیع شده نیز عمل می کند. یعنی حتی می توان تغییرات انجام شده بر روی چند پایگاه داده متفاوت را بی اثر نمود.

استفاده از این کلاس بسیار ساده می باشد و کافیست که قطعه کد های خود را داخل بلاک TranasctionScope بگذاریم.

using (TransactionScope scope= new TransactionScope(TransactionScopeOption.RequiresNew))
{
    // perform data access here

    scope.Complete();
}

با فراخوانی متد Complete در انتهای تمامی عملیات، تغییرات اعمال می شوند و در غیر اینصورت تغییرات بی اثر می شوند. به یاد داشته باشید که کلاس TranasctionScope در پس زمینه خود از امکانات +COM استفاده می کند و در نتیجه ملاحظات +COM در اینجا نیز باید مد نظر قرار گیرد. ضمنا برای استفاده از این کلاس باید ابتدا ارجاع (Reference) اسمبلی System.Transaction را به برنامه اضافه کنید.


 

روش دیگر اعمال Transaction بر روی اتصال (Connection) پایگاه داده می باشد.

اگر دقت کرده باشید در قطعه کدهایی که تاکنون نوشته ایم هیچ گاه اتصال پایگاه داده را باز یا بسته نکرده ایم (به جز در هنگام استفاده از ExecuteXmlReader). اگر بخواهیم یک Transaction را بر روی اتصال پایگاه داده اعمال کنیم باید شکل زیر عمل کنیم.

کلاس Database دارای متدی به نام CreateConnection می باشد که یک شیء از نوع DbConnection ایجاد کرده و برمی گرداند. کافیست که Transaction خود را (که در اینجا از نوع DbTransaction می باشد) بر روی این اتصال اعمال کنیم. به قطعه کد زیر توجه کنید.

using (DbConnection conn = db.CreateConnection())
{
    conn.Open();
    DbTransaction trans = conn.BeginTransaction();
    try
    {
        ....
        ....
        ....

        // execute commands, passing in the current transaction to each one
        db.ExecuteNonQuery(command1, trans);
        db.ExecuteNonQuery(command2, trans);
        ....
        trans.Commit(); // commit the transaction
    }
    catch
    {
        trans.Rollback(); // rollback the transaction
    }
}

در این روش باز و بسته نمودن اتصال به عهده خود ما می باشد و بنابراین پس از انجام عملیات باید اتصال را ببندیم. این عمل را با استفاده از بلاک using انجام داده ایم. اغلب متدهایی که دستورات را بر روی پایگاه داده اجرا می کنند دارای Overload هایی می باشند که یک پارامتر از نوع DbTransaction دریافت می کنند. برای اعمال تراکنش باید شیء DbTransaction را به این متد ها بفرستیم. در قطعه کد بالا برای متد ExecuteNonQuery این کار را انجام داده ایم.

پس از انجام عملیات، تراکنش را برای اعمال تغییرات Commit نموده ایم. اگر خطایی هنگام انجام عملیات پیش آید، ادامه کار به بلاک catch منتقل شده و تراکنش Rollback می شود.

در این مقاله ما برای تبدیل نمودن داده های بازیابی شده از پایگاه داده به اشیائی از نوع Studentاز روش پیمایش IDataReader در یک حلقه استفاده کردیم. کتابخانه Enterprise Library دارای دو متد به نام های ExecuteSprocAccessor و ExecuteSqlAccessor می باشد که این کار را در درون خود انجام می دهند. ما در این مقاله این روش را بررسی نکردیم و شما در صورت علاقه مند بودن می توانید در مورد آن تحقیق کنید.

پنج شنبه 16 آذر 1391  3:11 PM
تشکرات از این پست
hosseinb68
hosseinb68
کاربر طلایی1
تاریخ عضویت : بهمن 1389 
تعداد پست ها : 1269
محل سکونت : بوشهر

آموزش کتابخانه Microsoft Enterprise Library 5.0 - قسمت پنجم

تذکر مهم:

بلاک لاگ دارای مفاهیم و اصطلاحاتی می باشد که در عین سادگی بسیار مهم هستند و بدون یادگیری این مفاهیم امکان استفاده مؤثر از این بلاک وجود ندارد. در حقیقت پیچیدگی و نکته های این بلاک بیشتر در هنگام تصمیم گیری و کار با ابزار پیکربندی می باشد و نه هنگام کد نویسی. بنابراین توصیه می شود که این مقاله را با دقت و در زمان مناسب مطالعه نمایید. بر خلاف مقالات پیشین، روند یادگیری مفاهیم بلاک لاگ از آخر به اول می باشد. بنابراین توصیه می شود ابتدا مقاله را با دقت مطالعه نموده و سپس به سراغ مثال ها بروید و در نهایت برای اطمینان از درک صحیح مفاهیم مجددا مقاله را مطالعه کنید.

مقدمه:

به طور کلی اصطلاح لاگ نمودن (Logging) برای ثبت و ذخیره نمودن رخداد های به وجود آمده در یک برنامه استفاده می شود. این رخداد ها می توانند ورود یا خروج یک کاربر به سیستم، ارسال یک پیامک، محتوای خطای به وجود آمده در نرم افزار، یک هشدار امنیتی و بسیاری موارد دیگر باشند.

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

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

فرض کنید که وب سایتی نوشته اید که شبانه روز در حال ارائه خدمات به کاربران می باشد. اگر این وب سایت در ساعاتی خاص از نیمه شب عملکرد صحیحی نداشته باشد، چگونه باید از این مسئله آگاه شوید؟ و چگونه باید از دلیل به وجود آمدن این مشکل مطلع شوید؟

مثال دیگر این است که بار ها مشاهده کرده اید که بسیاری از شرکت های بزرگ ناگهان اعلام کرده اند که سرور های آن ها در تاریخی مشخص مورد حمله سایبری واقع شده اند و هکر ها تا چه میزان توانسته اند در سیستم نفوذ کنند و بسیاری جزئیات دیگر. آیا این اطلاعات بدون بررسی لاگ های سیستم بدست آمده اند؟

و آخرین مثال این است که فرض کنید برنامه ای تحت ویندوز نوشته اید که در برخی رایانه ها عملکرد صحیح دارد و در برخی دیگر خیر! چگونه باید پی به علت به وجود آمدن این مشکل و قسمت آسیب پذیر برنامه ببرید؟

صد ها مثال دیگر می توان برای اهمیت وجود قسمت لاگ در برنامه زد و به واقع باید گفت که تنها برنامه هایی نیاز به قسمت لاگ ندارند که به طور کل نیازی به پشتیبانی ندارند!

قبل از اینکه به سراغ بلاک لاگ (Logging Application block) در مجموعه Enterprise Library برویم بهتر است ابتدا نیاز های متداول در مورد لاگ نمودن اطلاعات را بررسی کنید.

آغاز:

بلاک لاگ در مجموعه Enterprise Library یکی از قدرتمندترین بلاک ها می باشد به طوریکه با چند خط پیکربندی و چند خط کد می توانیم اطلاعات بسیار مفیدی را در محل های مختلف لاگ نماییم.

ویژگی ها و مزایای استفاده از بلاک لاگ:

  • امکان ذخیره نمودن لاگ ها در رسانه های مختلف از قبیل پایگاده داده، فایل XML، فایل متنی، MSMQ، WMI، Windows Event Log، ارسال محتوای لاگ از طریق ایمیل و غیره.
    خوشبختانه امکان توسعه این قابلیت ها و ذخیره سازی در سایر رسانه ها نیز امکان پذیر می باشد.
  • براحتی می توان از طریق فایل پیکربندی قسمت های مختلف مربوط به لاگ را فعال یا غیر فعال نمود.
  • بسیاری از اطلاعات بدون اینکه نیاز باشد برای آن ها کد بنویسیم به شکل خود کار می توانند به اطلاعات لاگ اضافه شوند.
  • براحتی و با حتی دو خط کد نویسی می توان اطلاعات را در محل های مختلف لاگ نمود.
  • و بسیاری موارد دیگر که در ادامه با آن ها آشنا خواهید شد.

عناصر سازنده یک لاگ:

یک لاگ در سیستم عامل ویندوز به طور استاندارد دارای عناصری است که به طور پیشفرض تعدادی از آن ها نمایش داده می شوند و ما می توانیم با توجه به نیاز خود این عناصر را از خروجی لاگ حذف نموده یا عناصر دیگر را اضافه کنیم.

تصویر زیر یک لاگ از قسمت Windows Event Log را نشان می دهد (تصویر شماره 1).


تذکر:

برای نمایش لاگ های موجود در Windows Event Log باید به مسیر زیر رفته و پنجره Event Viewer را اجرا کنید.

Control Panel --> All Control Panel Items --> Administrative Tools

در قسمت زیر عناصر متداول موجود در یک لاگ را ملاحظه می کنید.

  • Title: عنوانی است که برای لاگ در نظر گرفته ایم. به طور مثال می توانیم لاگ های مربوط به هشدار های امنیتی را با عنوان Security Warnings ذخیره کنیم.
  • Message: متن کامل پیامی است که قرار است در لاگ ذخیره شود. به طور مثال این پیام می تواند متن کامل شرح یک خطای به وجود آمده در برنامه باشد.
  • Categories: دسته بندی هایی می باشد که لاگ به آن ها تعلق دارد. در این باره بعدا بحث خواهیم نمود.
  • Priority: اولویت لاگ را مشخص می کند و مقدار پیشفرض آن 1- می باشد. در این باره بعدا بحث خواهیم نمود.
  • Severity: مقداری است از یک نوع داده Enumration به نام TraceEventType که حاوی مقادیری از قبیل Information، Warning، Error، Critical و ... می باشد. اینگونه خصوصیت ها بدین جهت استفاده می شوند که در آینده هنگام بازیابی اطلاعات بتوانید لاگ ها را با استفاده از آن ها فیلتر کنید. به طور مثال ممکن است در نظر داشته باشید که ابتدا لاگ هایی را بررسی کنید که خصوصیت Severity آن ها برابر Critical باشد. مقدار پیشفرض این خصوصیت برابر Information می باشد.
  • EventId: یک شناسه عددی است که می توانیم به لاگ ها نسبت دهیم.
  • ActivityId: یک شناسه از نوع GUID است که اگر قابلیت tracing در لاگ فعال باشد به طور اتوماتیک (یا توسط ما) تولید شده و به این خصوصیت نسبت داده می شود. اگر قابلیت tracing غیر فعال باشد، مثدار Guid.Empty برگردانده می شود. در این مقاله این خصوصیت را مورد بررسی قرار نمی دهیم.
  • AppDomainName: اگر توسط برنامه نویس مقداری به این خصوصیت نسبت داده نشده باشد به طور پیشفرض نام AppDomain ای که برنامه در آن اجرا شده است را نگهداری می کند.
  • MachineName: اگر توسط برنامه نویس مقداری به این خصوصیت نسبت داده نشده باشد به طور پیشفرض مقدار Environment.MachineName را نگهداری می کند.
  • ManagedThreadName: اگر توسط برنامه نویس مقداری به این خصوصیت نسبت داده نشده باشد به طور پیشفرض مقدار Thread.CurrentThread.Name را نگهداری می کند.
  • ProcessId: اگر توسط برنامه نویس مقداری به این خصوصیت نسبت داده نشده باشد به طور پیشفرض مقدار شناسه Win32 ProcessId که برنامه در آن اجر شده است را نگهداری می کند.
  • ProcessName: اگر توسط برنامه نویس مقداری به این خصوصیت نسبت داده نشده باشد به طور پیشفرض نام پروسسی که برنامه در آن اجر شده است را نگهداری می کند.
  • Win32ThreadId: اگر توسط برنامه نویس مقداری به این خصوصیت نسبت داده نشده باشد به طور پیشفرض مقدار شناسه Win32 thread id را نگهداری می کند.
  • TimeStamp: اگر توسط برنامه نویس مقداری به این خصوصیت نسبت داده نشده باشد به طور پیشفرض مقدار DateTime.UtcNow را نگهداری می کند.
  • ExtendedProperties: کاربرد این خصوصیت بسیار جالب می باشد. در صورتی که بخواهیم اطلاعاتی را ذخیره کنیم که جزو هیچ کدام از دسته بندی های بالا نباشد می توانیم این اطلاعات را به شکل مجموعه ای از نام ها و مقادیر ذخیره نماییم. در آینده استفاده از این خصوصیت را نیز ملاحظه خواهید نمود.

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

ابتدا یک پروژه از نوع ویندوزی ایجاد نموده و اسمبلی های زیر را به آن اضافه نمایید.

  • Microsoft.Practices.EnterpriseLibrary.Common.dll
  • Microsoft.Practices.ServiceLocation.dll
  • Microsoft.Practices.Unity.dll
  • Microsoft.Practices.Unity.Interception.dll
  • Microsoft.Practices.EnterpriseLibrary.Logging.dll
  • Microsoft.Practices.EnterpriseLibrary.Data.dll
  • Microsoft.Practices.EnterpriseLibrary.Logging.Database.dll

تذکر:

اسمبلی ها را حتما از محل نصب کتابخانه Enterprise Library به برنامه اضافه کنید. برای بنده اسمبلی ها در مسیر زیر قرار دارند.

C:\Program Files\Microsoft Enterprise Library 5.0\Bin

سپس یک فایل پیکربندی (App.config) به برنامه اضافه کنید و آن را با استفاده از ویرایشگر Enterprise Library باز نمایید (روی فایل پیکربندی کلیک راست نموده و گزینه Edit enterprise Library V5 Configuration را انتخاب کنید).

از منوی Blocks گزینه Add Logging Settings را انتخاب کنید (تصویر شماره 2).


 

پس از انتخاب گزینه Add Logging Settings تنظیماتی مشابه شکل زیر را در ویراشگر ملاحظه می کنید. قسمت تنظیمات بلاک لاگ از 5 بخش مهم تشکلیل شده است که در شکل زیر این 5 بخش را متمایز نموده ایم(این تصویر را به خاطر داشته باشید - تصویر شماره 3).

 

با استفاده از این ویرایشگر می توانیم تمامی تنظیمات مورد نظر خود را انجام دهیم ولی خالی از لطف نیست که نگاهی به تغییرات انجام شده در فایل پیکربندی در خارج از ویرایشگر بیندازیم (تصویر شماره 4).

خوب، اکنون زمان بررسی این 5 قسمت معروف فرا رسیده است.

دسته بندی ها (Categories):

قسمت Categories (دسته بندی ها) و Special Categories (دسته بندی های ویژه) با عبارت Trace Source شناخته می شوند. برای اینکه یک عملیات لاگ را انجام دهیم باید ابتدا مشخص کنیم که این لاگ متعلق به کدام دسته بندی (ها) می باشد. دسته بندی (یا طبقه بندی) نمودن لاگ ها باعث این می شود که در آینده در صورت نیاز بتوانیم فقط لاگ های ذخیره شده متعلق به دسته بندی خاصی را بازیابی کنیم (مثلا لاگ های متعلق به دسته بندی خطاهای امنیتی).

هر لاگ می تواند متعلق به 1 دسته بندی (یا بیشتر) باشد. به طور مثال یک لاگ می تواند متعلق به دسته بندی های خطاهای امنیتی و لاگ های مربوط به مدیر سیستم باشد. نام گذاری دسته بندی ها بنا به سلیقه و نظر ما می باشد.

اگر به شکل 3 دقت کنید، ملاحظه می کنید که در قسمت Categories یک دسته بندی به طور پیشفرض به نام General به وجود آمده است. اگر هنگام کدنویسی زمانی که قصد نوشتن فرمان لاگ را داریم، مشخص نکنیم که این لاگ به چه دسته بندی (هایی) تعلق دارد، به طور پیشفرض برنامه لاگ از دسته بندی General استفاده می کند.

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

اکنون دسته بندی General را در ویرایشگر بسط می دهیم تا محتویات آن را بررسی کنیم (تصویر شماره 5).


 

همانطور که در شکل بالا ملاحظه می کنید، یک دسته بندی از 4 قسمت تشکیل شده است:

  1. Name: در این قسمت نام دسته بندی وجود دارد که به دلخواه ما می باشد.
  2. Auto Flush: مقدار این خصوصیت به طور پیشفرض true می باشد. مفهوم آن این است که هر زمانی که دستور لاگی که مربوط به این دسته بندی باشد صادر شود، انجام عملیات لاگ در همان لحظه به شکل فیزیکی انجام می شود (مثلا در یک فایل XML ذخیره می شود). ولی اگر مقدار این عبارت را برابر false کنیم، هنگام صدور دستور لاگ، این عملیات در حافظه ذخیره شده ولی به شکل فیزیکی انجام نمی شود تا زمانی که متد FlushContextItems از کلاس LogWriter فراخوانی شود (در آینده با این کلاس و امکاناتش آشنا خواهید شد). هنگام فراخوانی متد FlushContextItems تمامی لاگ هایی که در حافظه ثبت شده اند ولی به شکل فیزیکی انجام نشده اند، به شکل فیزیکی انجام شده و از حافظه پاک می شوند.
  3. Listeners: در ابتدای مقاله ذکر کردیم که یکی از ویژگی های برتر بلاک لاگ امکان ذخیره نموده لاگ ها در محل های مختلف می باشد. Listener ها یا به عبارت کاملتر Logging Target Listeners مشخص کننده این مطلب هستند که لاگ باید در کجا ذخیره شود. در شکل 3 ما قسمت Logging Target Listeners را با شماره 4 مشخص نموده ایم. در مورد جزئیات Logging Target Listeners بعدا بحث خواهیم کرد.

    هر دسته بندی می تواند یک یا بیشتر Listener داشته باشد. هنگامی که بلاک لاگ را برای اولین بار به برنامه اضافه می کنیم یک Listener به شکل پیشفرض با نام Event Log Listener ایجاد شده و به دسته بندی General اضافه می شود. بنابراین اگر ما یک لاگ را با استفاده از دسته بندی General انجام دهیم این لاگ توسط Event Log Listener شنود شده و در Event Log سیستم عامل ویندوز ذخیره می گردد.

    یک دسته بندی می تواند به تعداد دلخواه Listener داشته باشد و در نتیجه هنگام صدور دستور لاگ متعلق به این دسته بندی، محتویات لاگ به این Listener ها جهت ذخیره شدن در محل های مختلف ارسال می شود.
  4. Minimum Severity: این خصوصیت میزان شدت و سختی دسته بندی را مشخص می کند. مقادیر مجاز برای این خصوصیت می تواند یکی از مقادیر All ، Off ، Critical ، Error ، Warning ، Information ، Verbose ، ActivityTracing باشد که البته به طور پیشفرض مقدار آن All است.

    در بند قبلی (بند 3)  Listener ها را به شکل مختصر معرفی کردیم. Listener ها دارای خصوصیتی به نام Severity Filter هستند که دقیقا در تعامل با خصوصیت Minimum Severity دسته بندی ها عمل می کند. اگر مقدار خصوصیت Minimum Severity مربوط به یک دسته بندی برابر All باشد، هنگامی که یک دستور لاگ صادر می شود، این دسته بندی دستور لاگ را به تمامی Listener هایی که هنگام پیکربندی مشخص نموده ایم ارسال می کند.

    اما اگر مقدار خصوصیت Minimum Severity مربوط به یک دسته بندی مخالف All باشد، دستور لاگ فقط برای Listener هایی ارسال می شود که حداقل سختی آنها (Severity Filter آن ها) برابر یا بالاتر از سختی این دسته بندی باشد.

    شاید اکنون از خود بپرسیدکه اگر قرار باشد ویژگی Severity Filter یک Listener کمتر از Minimum Severity یک دسته بندی باشد، اصلا چرا این Listener را به آن دسته بندی اضافه کرده ایم؟! چون در هر صورت می دانیم که درخواست عمل لاگ به این Listener ارسال نمی شود! دلیل این موضوع این است که ویژگی های بلاک لاگ (و سایر بلاک ها) از طریق فایل پیکربندی به راحتی قابل تغییر است و پس از تولید نرم افزار و ارائه شدن به محل استفاده، خود مدیر سیستم می تواند با توجه به نیاز و شرایط خود این تنظیمات را برای مدتی تغییر دهد. یعنی تعدادی از Listener را از کار بیندازد یا برعکس.

    فراموش نکنید که کتابخانه Enterprise Library به واقع با رویکرد استفاده در نرم افزار های Enterprise طراحی شده است.

قبل از اینکه به ادامه بحث بپردازیم بهتر است که با یک مثال ساده نحوه انجام عمل لاگ را ملاحظه کنید. ابتدا فضاهای نامی زیر را به برنامه اضافه کنید.

using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;

اکنون به قطعه کد زیر دقت کنید.

LogWriter logWriter = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();

if (logWriter.IsLoggingEnabled())
{
    LogEntry logEntry = new LogEntry();
    logEntry.Title = "در این قسمت عنوان مربوط به لاگ قرار می گیرد";
    logEntry.Message = "در این قسمت متن کامل لاگ قرار می گیرد";
    logEntry.Categories = new List<string>() { "General" };
    logEntry.Priority = -1;
    logEntry.Severity = TraceEventType.Information;

    logWriter.Write(logEntry);
}

ابتدا یک شیء از کلاس LogWriterبا استفاده از مکانیزمی که در مقالات قبل شرح دادیم، ایجاد نموده ایم. سپس با استفاده از متد IsLoggingEnabled بررسی نموده ایم که قابلیت لاگ فعال می باشد یا خیر. البته به یاد داشته باشید که اگر قابلیت لاگ در فایل پیکربندی غیر فعال شده باشد (در آینده بیشتر در این مورد بحث خواهیم کرد)، در هر صورت عملیات لاگ انجام نخواهد شد ولی به هر حال از لحاظ برنامه نویسی و منطقی بهتر است که قبل از اجرای دستور لاگ، فعال بودن این قابلیت را با استفاده از متد IsLoggingEnabled چک کنیم.

سپس یک شیء از نوع کلاس LogEntry ایجاد نموده و اطلاعات لاگ را به آن اضافه کرده ایم. توجه داشته باشید که ما در مثال بالا فقط خصوصیات Priority، Severity، Title، Message و Categories را مقدار دهی نموده ایم و در نتیجه سایر خصوصیات دارای مقادیر پیشفرض خود هستند.

 نکته مهم در اینجا خصوصیت Categories می باشد. همانطور که قبلا هم ذکر شد، هر Log می تواند متعلق به یک یا چند دسته بندی باشد و به همین دلیل خصوصیت Categories یک لیست یا آرایه از نام ها را قبول می کند (در حقیقت یک ICollection<T>).

در نهایت متد Write از کلاس LogWriter را فراخوانی شده و محتویات لاگ را در EventLog ویندوز دخیره می شود. اگر هم اکنون به Event Viewer ویندوز مراجعه کنید، باید محتویات این لاگ را مشابه شکلی که در ابتدای مقاله نشان دادیم، مشاهده کنید.

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

دسته بندی های ویژه (Special Categories):

در شکل 3 این قسمت را با شماره 2 مشخص کرده ایم. این قسمت به طور پیشفرض و هنگام ایجاد، شامل 3 دسته بندی ویژه به نام های All Events، Unprocessed Category، Logging Errors & Warnings می باشد. این دسته بندی ها هم مشابه دسته بندی های معمولی هستند با این تفاوت که برای منظور های خاص طراحی شده اند و نمی توان به طور مستقیم در هنگام کد نویسی آن ها را مورد استفاده قرار داد.

قبل از اینکه به معرفی این قسمت ها بپردازیم، این سه قسمت را در ویرایشگر بسط می دهیم و در نتیجه شکل زیر پدیدار می شود(شکل شماره 6).


 

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

  1. All Events: اگر به شکل بالا دقت کنید، متوجه می شوید که در قسمت Listeners مربوط به این دسته بندی هیچ Listener ی وجود ندارد. یعنی این دسته بندی به طور پیشفرض هیچ کاری انجام نمی دهد تا اینکه شما در صورت نیاز، یک Listener به آن اضافه کنید. اما وظیفه این دسته بندی چیست؟ همانطور که مستحضر می باشید برای انجام عملیات لاگ به طور معمول باید هنگام کدنویسی، دسته بندی لاگ را مشخص کنیم تا آن دسته بندی با توجه به Listener هایی که دارد عملیات لاگ را انجام دهد. اما اگر به دسته بندی ویژه All Events یک Listener اضافه کنیم، به ازای تمامی دستورات لاگ های ما، متلعق به هر دسته بندی معمولی که باشند، این دسته بندی ویژه نیز عملیات لاگ را انجام می دهد و به همین دلیل است که نام آن را All Events نهاده اند.
  2. Unprocessed Category: خوب، با نگاهی به تصویر 3-1 براحتی متوجه می شوید که لیست Listener های این دسته بندی ویژه هم به طور پیشفرض خالی می باشد. اگر هنگام صدور فرمان لاگ در کدنویسی، این عملیات انجام نشود، محتویات لاگ برای این دسته بندی ارسال می شود و در صورتی که یک Listener به این دسته بندی اضافه کرده باشیم، عملیات لاگ توسط این دسته بندی ویژه، انجام می شود. ساده ترین دلیل آن می تواند این باشد که شما از نام یک دسته بندی در هنگام صدور فرمان لاگ استفاده کرده باشید که اصلا در تنظیمات فایل پیکربندی وجود نداشته باشد! حتما هم اکنون از خود می پرسید که چطور ممکن است که ما در کد از دسته بندی ای استفاده کرده باشیم که در فایل پیکربندی وجود ندارد؟! یک دلیل این می تواند باشد که مدیر سیستم در محل استفاده از نرم افزار، این دسته بندی را به هر دلیلی از فایل پیکربندی حذف نموده باشد.
  3. Logging Errors & Warnings: چه اتفاقی باید بیفتد اگر هنگام انجام عملیات لاگ یک خطا ایجاد شود. به طور مثال فرض کنید که عملیات ذخیره سازی لاگ بر روی پایگاه داده صادر شده است و به هر دلیلی پایگاه داده ما در دسترس نباشد! به یاد داشته باشید که بلاک لاگ به شکل یک ارزش افزوده به برنامه اضافه می شود و قرار است که بسیاری از مشکلات را حل کند و نه اینکه خود باری شود بر دوش برنامه!

    در صورتی که هنگام انجام عملیات لاگ، خطایی به وجود آید. محتوای لاگ به این دسته بندی ارسال می شود. این دسته بندی علاوه بر محتوای لاگ، محتوای خطای به وجود آمده را نیز لاگ می کند! تا بعدا مورد بررسی قرار گیرد. همانطور که در شکل 3-1 ملاحظه می کنید به طور پیشفرض یک Listener به آن اضافه شده است که همان Event Log Listener است و بنابراین محتویات این لاگ در Event Log ویندوز ذخیره می شود.

فیلتر ها (Log Filters):

در شکل 3 بخش مربوط به فیلتر ها را با شماره 3 مشخصی کرده ایم. بلاک لاگ این امکان را به مدیران سیستم داده است که عملکرد بلاک لاگ را بر اساس دسته بندی ها و اولویت ها و یا اینکه به طور کل غیر فعال کنند و این عمل از طریق قسمت فیلتر ها امکان پذیر است.

در حقیقت وقتی دستور انجام لاگ صادر می شود، دسته بندی مربوطه، ابتدا محتویات لاگ را برای قسمت فیلتر ها ارسال می کند و در صورتی که در این قسمت از انجام عمل لاگ جلوگیری به عمل نیاید، محتویات لاگ برای Litener های مربوطه ارسال می شود. به همین دلیل بود که قبلا هم اشاره کردیم که اگر حتی فعال بودن قسمت لاگ از طریق متد IsLoggingEnabled چک نشود باز هم پس از صدور دستور لاگ، ممکن است این عمل انجام نگردد (البته به خاطر داشته باشید که متد IsLoggingEnabled چک می کند که آیا به طور کل لاگ در سیستم فعال است یا خیر).

همانطور که در تصویر 3 ملاحظه می کنید قسمت Log Filters به طور پیشفرض خالی می باشد و شما باید فیلتر های مورد نظر خود را اضافه کنید. اکنون به معرفی انواع فیلتر ها می پردازیم.

 1. فیلتر بر اساس دسته بندی (Category Filter): برای اضافه کردن قسمت فیلتر بر اساس دسته بندی باید مطابق شکل زیر (تصویر شماره 7) بر روی علامت "+" کلیک نمایید. اکنون چهار گزینه برای انتخاب پدیدار می شوند که می توانید گزینه Add Category Filter را انتخاب کنید.


اکنون قسمت Category Filter مشابه زیر برای شما ایجاد شده است که به بررسی قسمت های مختلف آن می پردازیم (تصویر شماره 8).
 

  • Name: نام این فیلتر است که به دلخواه می باشد و شما می توانید نام مورد علاقه خود را انتخاب کنید.
  • Category: این قسمت به طور پیشفرض خالی می باشد. اگر بر روی علامت "+" در کنار این قسمت کلیک کنید، بخشی اضافه می شود که نام دسته بندی هایی که شما ایجاد نموده اید را نشان می دهد. با توجه به اینکه تاکنون ما فقط دسته بندی General را ایجاد نموده ایم، شما فقط این گزینه را می توانید انتخاب کنید. در حقیقت این قسمت مشخص می کند که فیلتر باید بر روی چه دسته بندی (هایی) اعمال شود.
  • Filter Mode: مقدار پیشفرض برای این خصوصیت AllowAllExceptDenied می باشد و مفهوم آن این است که به دسته بندی هایی که به این فیلتر اضافه نموده ایم، فیلتر اعمال می شود و پس از این اگر هر دستور لاگی مربوط به این دسته بندی (ها) صادر شود، عملیات متوقف شده و از انجام آن ممانعت به عمل می آید. ولی در مورد سایر دسته بندی ها هیچ مشکلی وجود ندارد.

    اگر مقدار این خصوصیت را به DenyAllExceptAllowed تغییر دهیم، دقیقا قضیه برعکس می شود. یعنی به تمام دسته بندی هایی که به این فیلتر اضافه ننموده ایم فیلتر اعمال می شود در حالیکه به دسته بندی هایی که به این فیلتر اضافه شده اند فیلتر اعمال نمی شود.
  • Type Name: این خصوصیت به شکل فقط خواندنی بوده و نام نوع فیلتر (در اینجا Category Filter) را نمایش می دهد.

 

2. فیلتر بر اساس اولویت (Priority Filter): برای اضافه نمودن این نوع فیلتر باید بر روی علامت "+" کنار Log Filters کلیک نموده و این بار گزینه Add Priority Filter را انتخاب کنید.

پس از اضافه نمودن فیلتر بر اساس اولویت شما با شکل زیر روبرو خواهید شد (تصویر شماره 9).

اگر به یاد داشته باشید هنگام  معرفی کردن محتویات یک لاگ، خصوصیتی به نام Priority را معرفی کردیم که اولویت لاگ را با عددی مشخص می کرد. این عدد به طور پیشفرض 1- می باشد. در قسمت فیلتر بر اساس اولویت می توانیم دستورات لاگ را بر اساس مقدار این خصوصیت فیلتر نماییم. اگر مقدار خصوصیت Priority یک لاگ برابر 1- باشد، هیچگونه فیلتری به آن اعمال نمی شود. اما اگر مقدار دیگری به آن نسبت دهیم در صورتی که این مقدار در بازه ی کمترین و بیشترین فیلتر ما قرار داشته باشد، عملیات لاگ انجام شده و در غیر اینصورت از انجام آن ممانعت به عمل می آید. برای درک بهتر مسئله بخش های مختلف فیلتر اولویت را بررسی می کنیم.

  • Name: نام این فیلتر است که به دلخواه می باشد و شما می توانید نام مورد علاقه خود را انتخاب کنید.
  • Maximum Priority: مقدار این خصوصیت به طور پیشفرض می باشد 2147483647 می باشد. در صورتی که مقدار اولویت لاگ کمتر یا مساوی این عدد باشد، عمل لاگ انجام می شود. این عدد بنا بر نیاز شما قابل تغییر می باشد.
  • Minimum Priority: مقدار این خصوصیت به طور پیشفرض مقدار آن 0 است. یعنی اگر دستور لاگی صادر شد که مقدار خصوصیت اولویت آن کمتر از 0 باشد، آن دستور فیلتر می شود و عمل لاگ انجام نمی شود (به یاد داشته باشید که عدد 1- استثناء است و لاگ هایی که اولویت آن ها 1- باشند هرگز توسط این فیلتر بلاک نمی شوند). این مقدار نیز بنا بر نیاز شما یا مدیر سیستم قابل تغییر می باشد.
  • Type Name: این خصوصیت به شکل فقط خواندنی بوده و نام نوع فیلتر (در اینجا Priority Filter) را نمایش می دهد.

به طور خلاصه لاگ هایی می توانند از این فیلتر عبور کنند که اولویت آن ها بین Minimum Priority و Maximum Priority باشد (به جز اولیت 1- که استثناء می باشد).

 

3. فیلتر فعال سازی لاگ (Logging Enabled Filter): دو فیلتر قبلی را که بررسی کردیم، متوجه شدیم که آن دو فیلتر بر اساس شرایطی عمل فیلتر را انجام می دهند. اما با استفاده از فیلتر فعال سازی لاگ می توانیم کل سیستم لاگ برنامه را فعال یا غیر فعال نماییم و وابسته به هیچ شرطی نیز نمی باشد.

برای اضافه نمودن این نوع فیلتر باید بر روی علامت "+" کنار Log Filters کلیک نموده و مانند شکل زیر گزینه Add Logging Enabled Filter را انتخاب کنید.

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

بررسی قسمت های مختلف این فیلتر:

  • Name: نام این فیلتر است که اختیاری می باشد و شما می توانید نام مورد علاقه خود را انتخاب کنید.
  • All Logging Enabled: اگر مقدار این خصوصیت true باشد، بلاک لاگ در سیستم فعال است و در صورتی که مقدار آن را به false تغییر دهیم، کل بلاک لاگ در سیستم غیر فعال شده و هیچگونه لاگی انجام نمی شود.
  • Type Name: این خصوصیت به شکل فقط خواندنی بوده و نام نوع فیلتر (در اینجا LogEnabledFilter) را نمایش می دهد.

همانند سایر قسمت های کتابخانه Enterprise Library قسمت فیلتر ها نیز قابل توسعه می باشد و از طریق Add Custom Logging Filter می توان فیلتر های سفارشی نوشته شده را به برنامه اضافه کرد. این موضوع خارج از اهداف این مقاله می باشد.

قسمت Logging Target Listeners:

در تصویر شماره 3 این قسمت را با عدد 3 مشخص نموده ایم. در مورد Listener ها قبلا بحث کرده ایم. Listener ها مسئول ذخیره سازی فیزیکی لاگ ها در یک رسانه می باشند. خوشبختانه با استفاده از بلاک لاگ امکان ذخیره شدن محتویات لاگ در رسانه های مختلف از قبیل فایل متنی، فایل XML، پایگاه داده SQL Server و MSMQ و WMI و Email و ... وجود دارد. و این قسمت هم قابل توسعه است و شما می توانید Listener های مورد نیاز خود را بنویسید.

در ادامه مقاله به معرفی چند Listener معروف می پردازیم و برای سایر Listener نیز منطق کار مشابه است و با صرف مقداری زمان می توانید پی به عملکرد آن ها ببرید.

Event Log Trace Listener: این Listener هنگام ایجاد بخش لاگ به طور پیشفرض به برنامه اضافه می شود و ما در این مقاله بار ها از آن استفاده نموده ایم. این Listener عمل لاگ نمودن در قسمت Event Log ویندوز را انجام می دهد. لازم به ذکر است برای عملکرد این Listener باید محیطی که برنامه در آن اجرا می شود مجوز های مورد نیاز جهت دسترسی به Event Log ویندوز را داشته باشد.

این قسمت را مطابق شکل زیر بسط می دهیم تا بخش های مختلف آن را بررسی کنیم (تصویر شماره 11).

معرفی قسمت های مختلف:

  • Name: یک نام دلخواه برای این Listener می باشد که می توان آن را به دلخواه تغییر داد.
  • Formatter Name: نام فرمت کننده خروجی این Listener می باشد که در آینده در این مورد صحبت خواهیم نمود.
  • Log Name: اگر در مثال هایی که تاکنون زده ایم دقت کرده باشید همه لاگ های ما در قسمت Application در Event Log ویندوز ذخیره شده است. با استفاده از این خصوصیت می توانیم تعیین کنیم که به کدام قسمت Event Log ویندوز اضافه شود از جمله Application و System
  • Machine Name: نام دستگاهی می باشد که باید لاگ در آن نوشته شود. مقدار پیشفرض آن "." است که نشانگر دستگاهی است که در حال حاضر برنامه بر روی آن اجرا شده است.
  • Severity Filter: حداقل میزان شدت و سختی این Listener است. قبلا در مورد این قسمت بحث نموده ایم. اگر به یاد داشته باشید دسته بندی ها نیز دارای خصوصیتی به نام Minimum Severity هستند. در صورتی که حداقل سختی مقدار این Listener بزرگتر یا مساوی Minimum Severity دسته بندی مربوطه باشد، عمل لاگ انجام توسط این Listener انجام می شود. به طور پیشفرض مقدار این خصوصیت All می باشد و بنابراین همه ی لاگ ها بدون مقایسه شدت و سختی لاگ می شوند.
  • Source Name: خصوصیتی به نام Source در یک لاگ وجود دارد که می توانیم اینجا آن را تعیین کنیم. به طور پیشفرض مقدار این خصوصیت برابر Enterprise Library Logging می باشد.
  • Trace Output Options: این خصوصیت اختیاری می باشد و مربوط به Listener هایی است که خروجی آن ها متنی نیست و از نوع باینری است (مانند WMI).

 

Flat File Trace Listener: برای اضافه کردن این قسمت کافیست که بر روی علامت "+" در سمت راست Logging Target Listeners کلیک نموده و گزینه Add Flat File Trace Listener را انتخاب کنید (تصویر شماره 12).


 

این Listener جهت ذخیره سازی لاگ در یک فایل متنی استفاده می گردد. اکنون آن را بسط داده و خصوصیات مختلف آن را بررسی می کنیم (تصویر شماره 13).


 

  • Name: یک نام  دلخواه برای این Listener می باشد که می توان آن را به دلخواه تغییر داد.
  • File Name: آدرس فایل متنی است که قرار است لاگ در آن ذخیره شود. دقت داشته باشید که حتما یک آدرس ثابت به این قسمت بدهید.البته استفاده از متغیر های استاندارد ویندوز مانند "%TEMP%" و "%WINDIR%"  نیز مجاز می باشد. در صورتی که از از علامت ".\" برای آدرس دهی استفاده کنید، فایل مربوطه در کنار فایل اجرایی برنامه شما ذخیره می شود (به طور مثال ".\trace.log" ) .
  • Formatter Name: بعدا در این مورد بحث خواهیم نمود.
  • Message Footer: عبارتی است که در پایان ذخیره سازی هر لاگ نوشته می شود و باعث این می شود که لاگ های مختلف در یک فایل متنی به راحتی از دیگری قابل تمایز باشند.
  • Message Header: عبارتی است که در ابتدای ذخیره سازی هر لاگ نوشته می شود و باعث این می شود که لاگ های مختلف در یک فایل متنی به راحتی از دیگری قابل تمایز باشند.

در قسمت زیر یک لاگ ذخیره شده در یک فایل متنی را ملاحظه می کنید (تصویر شماره 14).


 

XML Trace Listener: برای اضافه کردن این قسمت کافیست که بر روی علامت "+" در سمت راست Logging Target Listeners کلیک نموده و گزینه Add XML Trace Listener را انتخاب کنید.

همانطور که از نام این Listener قابل تشخیص است، وظیفه آن لاگ نمودن با فرمت XML است. اکنون این Listener را بسط می دهیم (تصویر شماره 15).

 همانطور که ملاحظه می کنید این Listener خصوصیت جدیدی ندارد. شکل زیر یک قسمتی از یک نمونه لاگ را نمایش می دهد (تصویر شماره 16).


 

Email Trace Listener: برای اضافه کردن این قسمت کافیست که بر روی علامت "+" در سمت راست Logging Target Listeners کلیک نموده و گزینه Add Email Trace Listener را انتخاب کنید.

اگر این Listener را بسط دهیم با شکل زیر روبرو می شویم (تصویر شماره 17).


 

خوب، خصوصیاتی که مشاهده می کنید همان مواردی هستند که برای ارسال ایمیل مورد نیازند و توضیح خاصی در مورد آن نیاز نیست. در صورتی که نشانگر ماوس را بر روی هر عنوان قرار دهید یک Tooltip به عنوان راهنما نمایش داده می شود.

Database Trace Listener: برای اضافه کردن این قسمت کافیست که بر روی علامت "+" در سمت راست Logging Target Listeners کلیک نموده و گزینه Add Database Trace Listener را انتخاب کنید.

اگر این Listener را بسط دهیم با شکل زیر روبرو می شویم.

با استفاده از این Listener می توان لاگ ها را در پایگاه داده ذخیره کرد. ابتدا باید جداول و پرویسجر های مربوط به پایگاه داده را ایجاد کنیم. یک فایل به نام LoggingDatabase.sql در محلی که سورس فایل های Enterprise Library را ذخیره نموده اید وجود دارد که حاوی اسکریپت مورد نیاز جهت ساخت جداول و پرویسجر های مربوطه استفاده می شود. برای بنده این فایل در آدرس زیر قرار دارد (این فایل همراه سورس کامل این مقاله از لینک بالای صفحه قابل دریافت می باشد):

C:\EntLib50Src\Blocks\Logging\Src\DatabaseTraceListener\Scripts

اسکریپت مربوطه دارای دو پروسیجر مهم به نام های Add Category و Write Log می باشد و به طور پیشفرض نیز در شکل بالا نمایش داده شده اند. در صورتی که بخواهید نام پروسیجر ها را در پایگاه داده تغییر دهید باید در این قسمت نیز این دو نام را تغییر دهید.

کاربرد سایر خصوصیات نیز کاملا مشهود می باشد. Database Instance Name، نام رشته اتصالی که باید در فایل پیکربندی ایجاد شود را نگهداری می کند.

در صورتی که علاقه مند به یادگیری عملکرد سایر Listener هستید، می توانید در اینترنت به دنبال منابع موجود بگردید.

معرفی فرمت کننده ها (Log Message Formatters):

خوب، آخرین مبحثی که قصد داریم در مورد آن صحبت کنیم، فرمت کننده ها می باشند. در تصویر شماره 3 این قسمت با عدد 5 مشخص کرده ایم.

Formatter ها همانطور که از نامشان مشخص می باشد جهت فرمت نمودن خروجی Listener ها جهت ذخیره سازی در محیط های مختلف استفاده می شوند (یعنی چینش و نحوه قرار گیری عناصر مختلف کنار هم مشخص می شود). همانطور که در شکل 3 مشخص شده است، هنگام ایجاد بلاک لاگ، یک فرمت کننده به نام Text Formatter به طور پیشفرض اضافه می شود. تاکنون تمامی Listener هایی که بررسی کردیم از این فرمت کننده جهت فرمت نمودن خروجی خود استفاده نموده اند.

یه طور کلی 3 نوع فرمت کننده در کتابخانه Enterprise Library 5 تعبیه شده است (و البته همچون قسمت های دیگر امکان نوشتن فرمت کننده های جدید نیز وجود دارد) و عبارتند از Text Formatter و XML Formatter و Binary Formatter.

Text Formatter: این فرمت کننده، پر استفاده ترین می باشد و جهت فرمت نمودن خروجی Listener هایی که استفاده می شود که خروجی آن ها از نوع متنی است. در شکل زیر فرمت کننده Text Formatter را بسط داده ایم (تصویر شماره 19).

همانطور که ملاحظه می کنید این فرمت کننده دارای خصوصیتی به نام Template می باشد. با استفاده از این خصیصه می توانید خروجی لاگ را جهت ذخیره سازی تغییر دهید. اگر روی دکمه سمت راست Template کلیک کنید پنجره Template Editor باز می شود و براحتی قادر خواهید بود که ساختار ذخیره سازی لاگ را تغییر دهید (تصویر شماره 20).

Binary Formatter: این فرمت کننده جهت فرمت نموده خروجی به شکل باینتری استفاده می شود و کار برد آن در WMI Trace Listener می باشد.

XML Formatter: این فرمت کننده در حقیقت نوع خاصی از فرمت کننده Text Formatter می باشد که به شکل درونی در XML Trace Listener استفاده می شود و به شکل آزاد در اختیار ما قرار ندارد.

برای اضافه نمودن Formatter های جدید کافیست که روی علامت "+" کنار عبارت Log Massage Formatter کلیک نمایید و گزینه ها مطابق شکل زیر مشخص می شوند (شکل شماره 21).


 

همانطور که قبلا بحث شد، Listener ها دارای خصوصیتی به نام Formatter Name هستند. این خصوصیت آن ها در حقیقت نام فرمت کننده مورد استفاده آن ها را مشخص می کند.

فرآیند استفاده از بلاک لاگ در یک نگاه:

پس از اضافه نمودن فرمت کننده های مورد نظر خود، می توانید به سراغ Listener ها رفته و Listener های مورد نظر خود را ایجاد کنید و سپس فرمت کننده ها را به آن ها اضافه کنید. اکنون می توانید به سراغ دسته بندی ها بروید و Listener ها را به آن ها اضافه کنید.

معرفی ویژگی Extended Properties:

اگر دقت کرده باشید در طول مقاله ما فقط خصوصیات از پیش تعریف شده ی کلاس LogEntry را مقدار دهی نموده ایم. این خصوصیات عبارت بودند از Title، Message، Priority و ... البته این خصوصیات به شکل جامعی تهیه شده اند و پاسخگوی اغلب نیاز های برنامه نویسان می باشند ولی ممکن است مواقعی پیش بیاید که نیاز داشته باشید اطلاعات دیگری را نیز همراه این خصوصیات لاگ کنید.

برای رفع این محدودیت در کلاس LogEntry خصوصیتی به نام ExtendedProperties وجود دارد.این خصوصیت یک Dictionary شامل زوج های مرتبی از نام ها و مقادیر دریافت می کند. برای استفاده از این خصوصیت باید اطلاعات مورد نظر خود را به شکل نام و مقدار بدان اضافه نماییم.

در مثال زیر ما دو خصوصیت جدید به نام extra1 و extra2 که شامل مقادیر value1 و value2 هستند را جهت لاگ کردن به این کلاس اضافه نموده ایم.

LogWriter logWriter = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();

if (logWriter.IsLoggingEnabled())
{
    LogEntry logEntry = new LogEntry();
    logEntry.Title = "در این قسمت عنوان مربوط به لاگ قرار می گیرد";
    logEntry.Message = "در این قسمت متن کامل لاگ قرار می گیرد";
    logEntry.Categories = new List<string>() { "General" };
    logEntry.Priority = 11;
    logEntry.Severity = TraceEventType.Information;

    //Add extra data to log
    Dictionary<string, object> dic = new Dictionary<string, object>();
    dic.Add(
"extra1", "value1");
    dic.Add(
"extra2", "value2");
    logEntry.ExtendedProperties = dic;


    logWriter.Write(logEntry);
}

شکل زیر قسمتی از اطلاعات لاگ شده توسط قطعه کد بالا را با استفاده از XML Trace Listener را نمایش می دهد (شکل شماره 22).


 

و در نهایت شکل زیر به زیبایی فرآیند لاگ نمودن با استفاده از کتابخانه Microsoft Enterprise Library را نشان می دهد (شکل شماره 23).
 

 

پنج شنبه 16 آذر 1391  3:13 PM
تشکرات از این پست
دسترسی سریع به انجمن ها