مطالب
آموزش (jQuery) جی کوئری 3#
در ادامه مطلب قبلی آموزش (jQuery) جی کوئری 2# به ادامه بحث می‌پردازیم.

انتخاب عناصر صفحه
در پستهای قبل (^ و ^) با بسیاری از توانایی‌ها و کارکردهای jQuery شامل توانایی‌های آن برای انتخاب عناصر موجود در صفحه تا تعریف توابع جدید و استفاده از آنها به محض آماده شدن صفحه آشنا شدیم.
در این پست و پست بعدی توضیحات تکمیلی در خصوص دو مورد از توانایی‌های jQuery و البته تابع ()$ خواهیم داشت که مورد اول، انتخاب عناصر صفحه با استفاده از انتخاب کننده‌ها و مورد دوم ایجاد عناصر جدید می‌باشد.
در بسیاری از مواقع برای تعامل با صفحه اینترنتی نیاز به تغییر دادن بخشی از یکی از اشیا موجود در صفحه داریم. اما پیش از آنکه قادر باشیم آنها را تغییر دهیم، ابتدا باید با استفاده از مکانیزمی شی مورد نظر را مشخص و سپس آن را انتخاب کنیم تا پس از آن قادر به اعمال تغییری در آن باشیم. بنابراین اجازه دهید تا به یک بررسی عمیق از راه‌های مختلف انتخاب عناصر صفحه و ایجاد تغییر در آنها بپردازیم.

1-انتخاب عناصر صفحه برای ایجاد تغییر

اولین قدم برای استفاده از هر گونه تابع jQuery، مشخص کردن و انتخاب عناصری است که می‌خواهیم تابع روی آن عناصر اعمال شود. گاهی اوقات انتخاب این مجموعه عناصر با یک توضیح ساده مشخص می‌شود، برای مثال "تمام عناصر پاراگراف موجود در صفحه". اما گاهی اوقات مشخص کردن این مجموعه نیاز به توضیح پیچیده‌تری دارد، برای مثال "تمام عناصر لیست در صفحه که دارای کلاس listElement هستند و لینکی دارند که اولین عضو آن لیست می‌باشد".
خوشبختانه jQuery یک مکانیزم بسیار قوی و قدرتمند ارایه کرده است که انتخاب هر عنصری از صفحه را به سادگی امکان پذیر می‌سازد. انتخاب کننده‌های jQuery از ساختار مربوط به CSS استفاده می‌کنند، بنابراین ممکن است شما هم اکنون با تعداد زیادی از آنها آشنا باشید . در ادامه شمار بیشتر و قدرتمندتری خواهید آموخت.
برای درک بهتر شما از مطالب مربوط به بخش انتخاب کننده ها، یک مثال آماده مختص به این مبحث، در قالب یک صفحه اینترنتی، را در فایل صفحه کارگاهی قرار داده ایم، این فایل در ادرس chapter2/lab.selector.htm قابل دسترسی می‌باشد. این مثال از پیش آماده و کامل (نوشته شده توسط نویسنده کتاب)، این امکان را به شما می‌دهد تا با وارد کردن یک رشته، به عنوان پارامتر انتخاب کننده، در همان زمان عنصر انتخاب کننده در صفحه را رویت کنید. زمانی که این صفحه را اجرا می‌کنید تصویری مانند زیر ظاهر خواهد شد.


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


1-1- استفاده از انتخاب کننده‌های ابتدایی CSS
برنامه نویسان وب برای اعمال فرمت‌های ظاهری گوناگون به بخش‌ها و عناصر مختلف یک صفحه اینترنتی، از ایک راه بسیار ساده، در عین حال قدرتمند و کارا استفاده می‌کنند که در تمام مرورگرهای مختلف نیز جوابگو باشد. این انتخاب کننده‌ها عناصر را بر اساس نام شناسه آنها، نام کلاس و یا ساختار سلسله مراتبی موجود در صفحه انتخاب می‌کنند.
در زیر به معرفی چند نمونه از این انتخاب کننده‌های ساده CSS می‌پردازیم:
  • a   : تمام عناصر <a> را انتخاب می‌کند.
  • specialID# : عنصری را که دارای ID با عنوان specialID باشد انتخاب می‌کند.
  • specialClass.               : عناصری را که دارای کلاس specialClass هستند انتخاب می‌کند.
  • a#specialID.specialClass : این عبارت عنصری را انخاب می‌کند که شناسه آن specialID باشد، به شرط آنکه این عنصر <a> باشد و دارای کلاس specialClass نیز باشد را انتخاب می‌کند.
  • p a.specialClass: تمام عناصر لینک (<a>) را که دارای کلاس specialClass باشند و درون یک عنصر پاراگراف (<p>) قرار گرفته باشند را انتخاب می‌کند.

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

نکته مثبت در مورد انتخاب کننده‌های CSS این است که از همین انتخاب کننده‌ها می‌توانیم در jQuery نیز استفاده کنیم. برای این کار تنها کافیست انتخاب کننده مورد نظر را به تابع ()$ ارسال کنیم. در زیر یک نمونه را مشاهده می‌کنید:

$("p a.specialClass")
به جز چند مورد خاص که استثنا وجود دارد، CSS3 و jQuery کاملا با هم سازگاری دارند. بنابراین انتخاب عناصر به این شکل طبیعی خواهد بود. به عبارتی دیگر هر عنصر که از این طریق توسط CSS انتخاب شود، همان انتخاب حاصل انتخاب کننده jQuery نیز خواهد بود. اما باید به این نکته توجه داشت که jQuery وابسته به CSS نیست و اگر مرورگری پیاده سازی استانداردی برای CSS نداشته باشد، انتخاب کننده jQuery به مشکل بر نمی‌خورد، بلکه jQuery انتخاب خود را به درستی انجام می‌دهد، چرا که jQuery از قوانین استاندارد W3C تبعیت می‌کند.

2-1- استفاده از انتخاب کننده‌های فرزند (Child) ، نگهدارنده (Container) و صفت (Attribute)

برای انتخاب کننده‌های پیشرفته تر، jQuery از جدیدترین مرورگرهایی که CSS را پشتیبانی می‌کنند، استفاده می‌کند که می‌توان به Mozilla Firefox, Internet Explorer 7, Safariو سایر مرورگرهای پیشرفته (مدرن) اشاره کرد. این انتخاب کننده‌های پیشرفته شما را قادر می‌سازند تا مستقیما فرزند یک عنصر را انتخاب کنید و یا از ساختار سلسله مراتبی عناصر صفحه، مستقیما به عنصر مورد نظر دسترسی داشته باشید و یا حتی تمام عناصری که یک صفت خاص را شامل می‌شوند، انتخاب کنید. گاهی اوقات انتخاب فرزندی از یک شی برای ما مطلوب است. برای مثال ممکن است ما به چند مورد از یک لیست احتیاج داشته باشیم، نه یک زیر مجموعه ای از آن لیست. به قطعه کد زیر که از صفحه کارگاهی این پست گرفته شده است دقت نمایید:

<ul>
   <li><a href="http://jquery.com">jQuery supports</a>
      <ul>
            <li><a href="css1">CSS1</a></li>
            <li><a href="css2">CSS2</a></li>
            <li><a href="css3">CSS3</a></li>
            <li>Basic XPath</li>
       </ul>
    </li>
    <li>jQuery also supports
        <ul>
             <li>Custom selectors</li>
             <li>Form selectors</li>
         </ul>
      </li>
</ul>
حال فرض کنید از این ساختار، لینک وب سایت jQuery مد نظر ماست و این کار بدون انتخاب سایر لینک‌های مربوط به CSS مطلوب است. اگر بخواهیم از دستور‌های انتخاب کننده CSS استفاده کینم، دستوری به شکل ul.myList li a خواهیم داشت. اما متاسفانه این دستور تمام لینک‌های این ساختار را انتخاب میکند، زیرا همه آنها لینک هایی در عنصر li می‌باشند. با نوشتن این دستور در صفحه کارگاهی خروجی به شکل زیر خواهد بود:
راه حل مناسب برای انتخاب چنین حالتی استفاده از انتخاب فرزند می باشد که به این منظور Parent (والد) و Child (فرزند)، به وسیله یک کاراکتر < از یکدیگر جدا می‌شوند:
p > a
این دستور تنها لینک (<a>) هایی را بر می‌گرداند که فرزند مستقیم یک عنصر <p> می‌باشند. بنابراین اگر در یک <p> لینکی در عنصر <span> معرفی شده باشد، این لینک انتخاب نمی‌شود، چرا که فرزند مستقیم <p> به حساب نمی‌آید. در مورد مثال لینک‌های موجود در لیست، می‌توانیم دستور زیر را به منظور انتخاب لینک مورد نظرمان استفاده کنیم:
ul.myList > li > a
 دستور انتخاب فوق از میان عناصر <ul>، عنصری را که دارای کلاس myList می‌باشد، انتخاب می‌کند و پس از آن لینکهایی (<a>) که فرزند مستقیم گزینه‌های آن هستند، برگردانده می‌شوند. همانگونه که در شکل زیر مشاهده می‌کنید لینک‌های زیرمجموعه عنصر <ul> انتخاب نمی‌شوند، زیرا فرزند مستقیم این عنصر محصوب نمی‌شوند.

انتخاب کننده‌های صفت نیز بسیار قدرتمند می‌باشند و ما را تواناتر می‌سازند، فرض کنید برای منظوری خاص قصد دارید به تمام لینک‌های موجود در صفحه که به مکانی خارج از این وب سایت اشاره دارند، رفتاری را اضافه کنید (مثلا مانند همین سایت به کنار آنها یک آیکن اضافه نمایید) . فرض کنید این کد (کد موجود در مثال کارگاهی) را در صفحه خود دارید:
<li><a href="http://jquery.com">jQuery supports</a>
    <ul>
          <li><a href="css1">CSS1</a></li>
          <li><a href="css2">CSS2</a></li>
          <li><a href="css3">CSS3</a></li>
          <li>Basic XPath</li>
     </ul>
</li>
موردی که یک لینک با اشاره به وب سایت خارجی را از سایر لینک‌ها متمایز می‌سازد، شروع شدن مقدار صفت href آن با //:http می‌باشد. انتخاب لینک هایی که مقدار href آنها با //:http آغاز می‌شود، به سهولت و از طریق دستور زیر صورت می‌پذیرد:
a[href^=http://]
این دستور باعث انتخاب تمام لینک هایی که مقدار صفت href آنها دقیقا با //:http آغاز می‌شود، می‌گردد. علامت ^ موجب می‌شود تابررسی، لزوما از ابتدای مقادیر صورت پذیرد و از آنجا که استفاده از این کاراکتر در سایر عبارات منظم به همین منظور صورت می‌پذیرد، به خاطر سپردن آن دشوار نخواهد بود.
می توانید این کد را در صفحه کار گاهی تست کنید.
را‌های دیگری برای استفاده از انتخاب کننده‌های صفت وجود دارد.

form[method]
این دستور تمام عناصر <form> را که یک صفت method دارند را انتخاب می‌کند.

input[type=text]
این انتخاب کننده تمام عناصر input را که type آنها برابر text با شد انتخاب می‌کند.
دستور زیر مثالی دیگر برای بررسی یک مقدار بر اساس کاراکترهای نخست آن می‌باشد:
div[title^=my]
همانطور که از دستور فوق بر میآید، عناصر div که مقدار title آنها با رشته my اغاز می‌شود، هدف این انتخاب کننده خواهد بود.
اما اگر بخواهیم تنها بر اساس کاراکتر‌های انتهایی انتخابی انجام دهیم، دستور مناسب چه خواهد بود؟ برای چنین منظوری مانند زیر عمل می‌کنیم:
a[href$=.pdf]
این دستور کاربرد زیادی برای شناسایی لنک‌های اشاره کننده به فایل‌های pdf دارد. ساختار زیر نیز زمانی استفاده می‌شود که یک عبارت منظم در جایی از یک صفت قرار گرفته باشد، خواه این عبارت از کاراکتر دوم آغاز شده باشد و یا از هرجای دیگر.
a[href*=jquery.com]
همانگونه که انتظار میرود این انتخاب کننده ، تمام لینک هایی که به وب سایت jQuery اشاره دارند را برمی گرداند.
فراتر از خصوصیات، بعضی مواقع ما می‌خواهیم بررسی کنیم که آیا یک عنصر شامل عنصر دیگری هست یا خیر. در مثال‌های قبلی فرض کنید ما می‌خواهیم بدانیم که آیا یک li شامل a هست یا خیر، jQuery با استفاده از انتخاب کننده‌های Container‌ها این را پشتیبانی می‌کند:
li:has(a)
این انتخاب کننده همه li هایی را برمی گرداند که شامل لینک (<a>) هستند. دقت کنید که این انتخاب گر مانند li a نیست، انتخاب گر دوم تمامی لینک هایی را که در li هستند بر میگرداند اما دستور بالا li هایی را بر میگرداند که دارای لینک (<a>) هستند.

تصویر زیر انتخاب گرهایی را نشان میدهد که ما می‌توانیم در jQuery استفاده نماییم.

انشالله در پست‌های بعدی ادامه مباحث را بررسی خواهد شد.
مطالب
اصول پایگاه داده - تراکنش ها
در این مقاله آموزشی قصد داریم به یکی از مهمترین و اساسی‌ترین مفاهیم تعریف شده در پایگاه داده بنام تراکنش‌ها بپردازیم. بعنوان تعریف می‌توان اینگونه بیان نمود که تراکنش یک واحد کاری منطقی است که عملی را بر روی پایگاه داده انجام می‌دهد. عموما تراکنش‌ها دنباله ای از عملیات پایگاه داده هستند که رویه هم رفته انجام یک کار یا وظیفه را بر عهده دارند. نکته مهمی که در مورد تراکنش‌ها مطرح می‌شود اینست که آنها باید به گونه ای مدیریت شوند که پایگاه داده را از یک وضعیت سازگار و درست (consistent) به وضعیت سازگار دیگری ببرند. به بیان دیگر اگر تراکنش از چند عملیات تشکیل شده باشد، پس از پایان اجرای تمامی عملیات مربوط به تراکنش نباید در داده‌های پایگاه داده هیچ تناقضی با قوانین پایگاه داده (integrity rules) بوجود بیاید. مزیت استفاده از تراکنش نیز همین مسئله است که به توسعه دهنده نرم افزار این اطمینان را می‌دهد که صحت و درستی پایگاه داده در اثر اجرای دستورات او از بین نخواهد رفت. علاوه بر آن اگر در حین اجرای یکی از دستورات خللی ایجاد گردد، پایگاه داده دوباره به وضعیت سازگار قبلی خود باز گردانده خواهد شد. نسل‌های اولیه سیستم‌های مدیریت پایگاه داده فاقد پیاده سازی تراکنش بودند و بهمین دلیل توسعه دهندگان کار بسیار مشکلی در شبیه سازی این واحد‌های یکپارچه منطقی داشتند. خوشبختانه اکثر DBMS‌های امروزی این مفهوم مهم را پشتیبانی می‌کنند و نیازی به نگرانی در مورد پیاده سازی آن نیست. تنها کاری که لازم است انجام گیرد کسب مهارت در زمینه استفاده از آنهاست.
تعریف تراکنش‌ها و مشخص کردن عملیات موجود در آنها اغلب توسط خود توسعه دهنده برنامه صورت می‌گیرد. اوست که تعیین می‌کند تراکنشش باید چه عملیاتی را با چه ترتیبی انجام دهد. اما در کنار این قسم از تراکنش‌ها که توسط کاربران تعریف می‌شود، تراکنش‌های دیگری نیز وجود دارند که توسط خود سیستم مدیریت پایگاه داده تعریف می‌شوند. به این قبیل تراکنش‌ها که واحد‌های کاری بسیار کوچک و عموما تجزیه ناپذیری هستند تراکنش‌های خودکار یا auto transactions گفته می‌شود. بعنوان مثال اگر ما تراکنشی را تعریف کرده باشیم که شامل یک عمل خواندن و یک عمل درج باشد، در هنگام اجرا سیستم این تراکنش را به دو تراکنش کوچکتر می‌شکند که در یکی عمل خواندن و در دیگری عملی نوشتن و درج را انجام می‌دهد. البته توجه داشته باشید که اگر چه این دو عملیات جدا و مستقل از هم اجرا می‌شوند اما رابطه منطقی آنها با یکدیگر  حفظ می‌شود و در صورت خللی در یکی از آنها اثر دیگری نیز بازگردانده شده و پایگاه داده دوباره به حالت قبل از جرا برگردانده می‌شود. به این کار عمل undo شدن تراکنش گفته می‌شود. 
 
گفتیم که تعریف تراکنش توسط کاربر صورت می‌پذیرد و مدیریت آن بر عهده پایگاه داده قرار می‌گیرد. در این میان نکته حائز اهمیتی وجود دارد که در اینجا باید به آن اشاره شود. اندازه تراکنش نقشی بسیار موثر در کارایی پایگاه داده ایفا می‌کند. توجه داشته باشید که اندازه تراکنش‌ها نباید خیلی بزرگ باشد. چراکه منجر به بزرگ شدن بیرویه فایل مربوط به ثبت وقایع پایگاه داده (log file) می‌گردد. تمامی علیات تاثیر گذار بر روی پایگاه داده در این فایل ثبت می‌شوند تا در موقع لزوم بتوان با استفاده از عمل بازیابی و ترمیم پایگاه داده (recovery) را انجام داد. بزرگ بودن این فایل در هنگام ترمیم می‌تواند بر روی کارایی تاثیر گذار باشد. علاوه بر این موضوع اندازه تراکنش‌ها اثر سوء دیگری نیز می‌تواند در پی داشته باشد و آن محدود نمودن درجه همروندی است. یعنی اگر اندازه تراکنش بیش از حد معمول باشد ممکن است بر روی تعداد تراکنش هایی که می‌توانند بطور موازی و همزمان اجرا شوند تاثیر منفی بگذارد. چرا که معمولا در آغاز تراکنش بر روی منابعی که مورد استفاده تراکنش قرار می‌گیرد قفل گذاری می‌شود تا بگونه ای مسئله نواحی بحرانی حل شود. این قفل‌ها زمانی آزاد می‌شوند که تمامی عملیات داخل تراکنش بطور کامل اجرا شده باشند یا اینکه مشکلی در حین اجرا بوجود آید. در این صورت هرچه تراکنش بزرگ‌تر باشد اجرای آن بیشتر طول خواهد کشید و در نتیجه قفل‌های آن نیز دیر‌تر آزاد می‌شوند. بدین ترتیب سایر تراکنش هایی که می‌خواهند از منابع مشترک استفاده کنند باید تا پایان اجرای تراکنش بزرگ ما منتظر بمانند. این مسئله یعنی کاهش درجه اجرای موازی با همروندی که اگر در سیستم‌های بزرگ به آن دقت نشود به گلوگاهی تبدیل خواهد شد و کارایی را به نحو قابل توجهی کاهش می‌دهد.
 
 تعریف تراکنش‌ها :
بدنه اصلی هر تراکنش را چهار کلمه کلیدی تشکیل می‌دهند که البته ممکن است صریحا در تعریف توسط کاربر لحاظ نشوند اما این چهار کلمه کلیدی باید در تمامی تراکنش‌ها چه بصورت صریح و چه بصورت ضمنی آورده شوند. این کلمات عبارتند از BEGIN TRANSACTION، END TRANSACTION، ROLLBACK و COMMIT. کلمات کلیدی BEGIN TRANSACTION و END TRANSACTION  همانطور که از نامشان پیداست آغاز و پایان یک تراکنش را نشان می‌دهد. اینکه تراکنش از چه نقطه ای آغاز و در چه نقطه ای به پایان رسیده است برای مدیریت آن بسیار مهم و حیاتی است بخصوص در مواقعی که در حین انجام مشکلی پیش بیاید. از کلمه کلیدی ROLLBACK هنگامی استفاده می‌کنیم که بخواهیم تغییراتی که تا این لحظه بر روی پایگاه داده صورت گرفته است را مجددا بی اثر کنیم و پایگاه داده را به حالت پیش از شروع تراکنش بازگردانیم. توجه داشته باشید که در برخی از مواقع ممکن است این کلمه را خودمان در بدنه تراکنش مستقیما قرار دهیم. بعنوان مثال یک خطای منطقی را در بخشی از روال انجام تراکنش با یک عبارت شرطی تشخیص می‌دهیم و با استفاده از ROLLBACK به مدیریت پایگاه داده اعلام می‌کنیم که عملیات بازگردانی را انجام بده. گاهی ممکن است ما صریحا این کلمه را در تراکنش نیاورده باشیم اما درحین انجام تراکنش خطایی رخ دهد، در این صورت خود سیستم مدیریت پایگاه داده خطا را شناسایی کرده و عملیات مربوط به ROLLBACK را انجام می‌دهد تا صحت و سازگاری پایگاه داده حفظ گردد. کلمه کلیدی COMMIT نیز باید در انتهای تراکنش آورده شود تا به مدیریت پایگاه داده اعلام شود که عملیات کامل شده است و تغییرات باید در پایگاه داده بطور فیزیکی اعمال شوند. توجه داشته باشید که تا زمانی که مدیریت پایگاه داده به دستور COMMIT نرسیده باشد، تغییرات را جهت اعمال بر روی حافظه فیزیکی به واحد مدیریت حافظه نمی‌دهد و بنابراین این تغییرات تا پیش از COMMIT از چشم سایر کاربران مخفی خواهد ماند.
 
نکته ای که در اینجا وجود دارد این است که فرمان COMMIT به معنی این نیست که بلافاصله تغییرات بر روی دیسک و حافظه جانبی نوشته می‌شود. بلکه به این معنی است که تمامی عملیات تراکنش با موفقیت انجام شده است و سیستم مدیریت پایگاه می‌تواند آنها را برای نوشته شدن در حافظه جانبی به واحد مدیریت حافظه تحویل دهد. در اینجاست که یکی دیگر از پیچیدگی‌های طراحی سیستم مدیریت پایگاه داده روشن می‌شود و آن اینست که این سیستم باید بنحوی این داده‌ها را در فاصله بین COMMIT و نوشته شدن در حافظه برای سایر کاربران قابل مشاهده نماید. 
 
در ادامه نمونه ای از یک تراکنش را مشاهده می‌کنید :
BEGIN TRANSACTION;
INSERT INTO SP RELATION {S#  S#(‘S5’), P#  P#(‘P1’), 
                    QTY  QTY(1000)}};
IF any error occurred THEN GOTO UNDO; END IF;
UPDATE P WHERE P# = P#(‘P1’)
    TOTAL:=TOTAL + QTY(1000);
IF any error occurred THEN GOTO UNDO; END IF;
COMMIT;
GOTO FINISH;
UNDO:  ROLLBACK;
FINISH: RETURN;
همانطور که مشاهده می‌کنید تراکنش بالا دارای تمامی بخش‌های اصلی تراکنش که ذکر شد می‌باشد. البته این امکان وجود دارد که صراحتا این کلمات را در تعریف بدنه تراکنش نیاوریم. بعنوان مثال می‌توان از آوردن COMMIT صرف نظر کرد. در این صورت خود سیستم مدیریت پایگاه داده پس از اجرای آخرین دستور تراکنش در صورتی که هیچ خطایی رخ نداده باشد بطور خودکار عمل COMMIT را انجام می‌دهد. این امر در مورد ROLLBACK و END نیز صادق است. اما در مورد BEGIN TRANSACTION نکته ای وجود دارد و آن اینست که ما باید به پایگاه داده اعلام کنیم که بطور خودکار در پایان یک تراکنش برای شروع تراکنش بعدی BEGIN TRANSACTION را لحاظ کند. این کار را باید با دستور SET IMPLICIT TRANSACTION ON انجام دهیم.
گفتیم که وقوع خطا می‌تواند توسط برنامه نویس شناسایی شود و یا توسط سیستم. یک نمونه از تشخیص خطا توسط برنامه نویس را در مثال بالا مشاهده می‌کنید. عموما دراین قبیل خطا‌ها پس از انجام عمل ROLLBACK تراکنش UNDO شده و اجرای آن متوقف می‌شود که اصطلاحا می‌گوییم تراکنش ABORT می‌شود. اما در مورد خطاهایی که خود سیستم تشخیص می‌دهد وضع به این منوال نیست. در شرایط خطا، سیستم پس از UNDO کردن تراکنش عموما آن را ABORT نمی‌کند بلکه مجددا اجرا می‌کند که به این عمل REDO گفته می‌شود. در بخش‌های بعدی بطور کامل در مورد دو عمل REDO  و UNDO بحث خواهیم کرد.
 
ویژگی‌های تراکنش‌ها :
هر تراکنشی که در سیستم اجرا میشود باید دارای چهار ویژگی باشد. در حقیقت این ویژگی‌ها باید به نحوی تامین شوند تا مقصود و هدف کلی تراکنش‌ها که بردن پایگاه داده از یک وضعیت صحیح به وضعیت صحیح دیگری است برآورده شود. در ادامه هر کدام را یک به یک شرح می‌دهیم :
 
Atomicity:
اولین ویژگی ای که یک تراکنش باید داشته باشد اینست که اثری که بر روی پایگاه داده ما می‌گذارد اثری کامل و بدون نقص باشد. به این معنا که اگر قرار است مجموعه از عملیات تغییراتی را اعمال کنند باید تمامی آن تغییرات بر روی جداول اعمال شوند. در صورتی که حتی یکی از عملیات با مشکل مواجه شود باید تاثیرات عملیات قبلی بازگردانده شوند. به بیانی ساده‌تر در تراکنش یا تمامی عملیات باید بطور کامل انجام شوند و یا هیچ یک از آنها نباید اجرا شده و اثرگذار باشند. به این ویژگی Atomicity گفته می‌شود.
 
توجه داشته باشید که در حین اجرای یک تراکنش احتمالا پایگاه داده به وضعیت غیر سازگار و نادرست خواهد رفت. یکی از وظایف سیستم مدیریت پایگاه داده اینست که این وضعیت ناسازگار را از دید سایر تراکنش‌ها مخفی بسازد تا زمانی که تراکنش COMMIT شود.
 
در مورد Atomicity در برخی مقالات و مطالب آموزشی گفته می‌شود که این مفهوم یعنی تراکنش نباید قابل شکسته شدن باشد که این تعریف چندان صحیحی از Atomicity نمی‌باشد. چراکه یک تراکنش در حین اجرا ممکن است بار‌ها و بارها شکسته شود و یا از یک تراکنش بر روی تراکنش دیگری سوئیچ شود. بنابراین مراد از Atomicity همان واحد کاری کامل است نه واحد کاری غیر قابل شکسته شدن.
 
 
Consistency:
تراکنش باید تغییرات را به گونه ای اعمال کند که پایگاه داده را از وضعیت صحیح به وضعیت صحیح دیگری ببرد.از آنجا که صحت پایگاه داده را قوانین جامعیت پایگاه داده (integrity rules) تضمین می‌کنند بنابراین تراکنش باید تغییرات را بگونه ای اعمال کند که این قوانین نقض نشوند. به این خاصیت از تراکنش‌ها Consistency گفته می‌شود.
 
Isolation:
عموما برنامه‌های مبتنی بر پایگاه در دنیای واقعی برنامه هایی چند کاربره هستند که در برخی از آنها ممکن است میلیون‌ها تراکنش بطور همزمان با یکدیگر در حال اجرا باشند. در چنین حجم بالایی یکی از مسائلی که مطرح می‌شود اینست که تراکنش‌های موازی تاثیر سوئی بر روی یکدیگر نداشته باشند. بعنوان مثال یکی از مشکلاتی که در اجرای همروند و موازی تراکنش‌ها ممکن است رخ دهد مشکل lost update می‌باشد. بر همین اساس یکی دیگر از ویژگی هایی که یک تراکنش باید داشته باشد که اینست که اثر سوئی بر روی تراکنش‌های همروند دیگر نداشته باشد. به این ویژگی Isolation گفته می‌شود.
در مورد ایزولاسیون (isolation) تراکنش‌ها باید گفت که ایزولاسیون سطوح و درجه بندی هایی دارد که هر کدام از این سطوح مشخص می‌کنند که تراکنش‌ها تا چه حدی اجازه دارند بر روی هم تاثیر گذار باشند. در واقع این سطوح، میزان عایق بندی تراکنش‌ها را نسبت به یکدیگر مشخص می‌کنند. هرچه درجه ایزولاسیون بالاتر باشند به این معنی است که تراکنش‌ها تاثیر کمتری بر روی یکدیگر خواهند داشت. خوب در ظاهر ممکن است این قضیه بسیار خوب در نظر بیاید چرا که به ما اطمینان  می دهد که اثر ناخواسته ای بر روی یکدیگر نخواهند داشت. اما باید این نکته را نیز در نظر بگیریم که هر چه درجه ایزولاسیون بالاتر باشد درجه همروندی (concurrency) پایین می‌آید و این به معنای کاهش امکان پردازش موازی تراکنش‌ها می‌باشد. این مسئله در مورد پایگاه‌های داده بسیار بزرگ که میلیون‌ها تراکنش همزمان در خواست اجرا داده می‌شوند به یک مسئله بحرانی و یک گلوگاه می‌تواند تبدیل شود. بنابراین تعیین درجه ایزولاسیون بسیار مهم است و باید با درنظر گرفته شرایط پروژه انجام گیرد. 
اینکه پایگاه داده ما در چه سطحی از ایزولاسیون باید عمل نماید توسط کاربر تعیین می‌شود. البته بحث در مورد ارجای موازی تراکنش‌ها و ایزولاسیون آنها بسیار مفصل است و انشاالله در مطلبی دیگر به آن خواهیم پرداخت.
 
 
Durability:
تغییراتی که تراکنش‌ها بر روی پایگاه داده می‌گذارند باید بعد از COMMIT شدن آن پایدار و قابل مشاهده باشند. به این خاصیت durability گفته می‌شود.
 
وضعیت‌های یک تراکنش :
تراکنش‌ها در سیستم همانند یک موجودیت (entity) فعال است هستند. همانطور که می‌دانید ساده‌ترین موجودیت فعال در سیستم فرآیند‌ها (process) می‌باشند که cpu را بعنوان یک ابزار در اختیار گرفته و وظایفی را انجام می‌دهند. تراکنش نیز یک موجودیت فعال می‌باشد و همانند سایر موجودیت‌های فعال دارای وضعیت هایی (state) می‌باشند که در ادامه هریک شرح داده شده اند :
 
فعال (Active) : تراکنشی که در حالت اجرا است در وضعیت فعال می‌باشد.
کامیت جزئی (Partially Committed): پس از اجرای آخرین دستور تراکنش به وضعیت کامیت جزئی می‌رود.
شکست (Failed): در این وضعیت، در روند اجرا خطایی رخ داده و اجرای ادامه تراکنش امکان پذیر نمی‌باشد.
خاتمه (Aborted): پس از تشخیص خطا تراکنش می‌تواند به وضعیت Aborted که در انجا اجرا متوفق شده و تغییرات ROLLBACK می‌شوند.
Committed: در این وضعیت اجرای تراکنش با موفقیت انجام شده و تراکنش پایان می‌پذیرد.
 
در ادامه نمودار حالت تراکنش‌ها نشاد داده شده است :


نکته ای که در اینجا لازم به ذکر است اینست که در حالت پس از حالت شکست به دو شکل امکان ادامه کار وجود دارد. در صورتی که خطای منطقی در تراکنش دیده شود که عموما توسط کاربر تشخیص داده می‌شود تراکش پس از شکست به حالت خاتمه برده می‌شود و کار تمام است. اما در برخی از شرایط خطایی سیستم توسط خود سیستم رخ می‌دهد. که در چنین حالاتی پس از شکست تراکنش مجددا تراکنش ممکن است به حالت فعال برگردانده شود و اجرای ان دوباره از ابتدای تراکنش شروع شود. به این وضعیت اصطلاحا REDO شدن تراکنش گفته می‌شود که در بخش RECOVERY و ترمیم پایگاه داده باید به آن پرداخته شود.
 
اعمال زمان COMMIT:
در زمان COMMIT (بصورت صریح و یا ضمنی)  باید اعمالی انجام شود که در اینجا به آن می‌پردازیم. اولین کاری که صورت می‌گیرد اینست که سیگنالی به DBMS ارسال می‌شود مبنی بر اینکه تراکنش با موفقیت به پایان رسیده است. پس از اینکار سیستم مدیریت پایگاه داده شروع به آزاد کردن قفل هایی می‌کند که در طول اجرای تراکنش بر روی منابع مختلف پایگاه داده زده شده است تا از تاثیر سوء تراکنش‌ها بر روی یکدیگر جلوگیری به عمل آید. علاوه بر کار ذکر شده تغییراتی که توسط تراکنش داده شده است باید پایدار و قابل رویت توسط سایر تراکنش‌ها گردد.
همانطور که در بخش ابتدایی این مطلب آموزشی اشاره کردیم COMMIT به معنی نوشته شدن تغییرات بر روی دیسک سخت نیست. سیستم مدیریت پایگاه داده تنها درخواست نوشتن داده‌ها را به سیستم مدیریت حافظه می‌دهد و نوشتن ان بر عهده مدیریت حافظه می‌باشد. سیستم مدیریت پایگاه داده باید اطلاع داشته باشد که چه تغییراتی نوشته شده است و چه تغییراتی هنوز در حافظه نوشته نشده است. بنابراین یکی دیگر از پیچیدگی‌های طراحی سیستم‌های مدیریت پایگاه داده اینست که تغییراتی را برای سایرین قابل رویت کند که هنوز در حافظه سخت نوشته نشده است.
 
اعمال زمان ROLLBACK:
در زمان ROLLBACK ناموفق بودن تراکنش باید به DBMS اطلاع داده شود. پس از انکه سیستم مدیریت پایگاه داده مطلع شد تمامی تغییرات اعمال شده تا آن لحظه را UNDO می‌کند. البته توجه داشته باشید که در این زمان همانند زمان COMMIT قفل‌ها نیز آزاد می‌شوند تا سایر تراکنش‌ها بتوانند از منابع در اختیار این تراکنش استفاده کنند و درجه همروندی پایین نیاید.
 
پردازش پیام‌ها در زمان اجرای تراکنش‌ها :
به مثال زیر توجه کنید. 

 Read Sav_Amt
  Sav_Amt := Sav-Amt - 500
    if Sav-Amt <0 then do
       put (“insufficient fund”)
       rollback
       end
    else do
      Write Sav_Amt
      Read Chk_Amt
      Chk_Amt := Chk_Amt + 500
      Write Chk-Amt
      put (“transfer complete”)
End transaction
در تراکنش بالا مبلغ 500 دلار از حساب فردی برداشته شده و به حساب دیگر او منتقل می‌شود. همانطور که مشاهده می‌کنید در خلال اجرای یک تراکنش ممکن است پیام هایی را به کاربر نمایش دهیم. حال در نظر بگیرید که در حین اجرا ما پیامی را در خروجی نمایش می‌دهیم و پس از آن تراکنش با شکست مواجه شده و ROLLBACK می‌گردد. در این شرایط پیامی به کاربر مبنی بر انتقال موفق نمایش داده شده است در حالی که در عمل تراکنش با شکست رو به رو شده است. برای حل این مشکل در ضمن کار پیام‌های مختلفی که در خروجی باید نمایش داده شوند بافر می‌شوند تا پس از COMMIT یا ROLLBACK شدن به کاربر نمایش داده شوند. توجه داشته باشید که در زمان  بافر کردن پیام ها، انها در دو گره پیام‌های مربوط به COMMIT و پیام‌های زمان ROLLBACK تقسیم می‌شوند تا هرکدام در شرایط خود نمایش داد شوند. این عمل توسط زیر سیستمی از DBMS بنام سیستم مدیریت ارتباطات داده ای (Data Communication Manager) انجام می‌گیرد.
 
انواع تراکنش‌ها :
تراکنش‌ها انواع و اقسام مختلفی دارند که به سبب پیچیدگی بعضی از آنها به لحاظ پیاده سازی ممکن است آنها را در برخی از پایگاه داده‌ها نداشته باشیم.
 
Flat Transactions:
ساده‌ترین نوع تراکنش‌ها می‌باشند که در تمامی پایگاه‌های داده پشتیبانی می‌شوند و مثال هایی که تا کنون در این نقاله زده شد از این دست می‌باشند.
 
Distributed Transactions:
این قبیل تراکنش‌ها مربوط به پایگاه داده‌های توزیع شده می‌باشند که داده‌های آنها بر روی ماشین‌های مختلفی قرار دارند. بر روی هریک از این ماشین‌ها ممکن است DBMS‌های مختلفی نیز نصب شده باشد که هر یک سیستم مدیریتی مربطو به خود را دارند. از آنجایی که هر یک از این ماشین‌ها یک سیستم مدیریت پایگاه داده مستقل دارند بنابراین قوانین جامعیتی محلی ای را نیز باید لحاظ نمایند. البته باید توجه داشت که علاوع بر این قوانین محلی یک سری قوانین سراسری نیز وجود خواهد داشت که مربوط به کل پایگاه داده توزیع شده می‌باشد. بعنوان مثال سیستم در یکی سیستم دانشگاهی که در شهر‌های مختلفی توزیع شده است، ممکن است بخواهیم تعداد کل دانشجویان ثبت نام شده در سیستم از هزار نفر بیشتر نباشد. عموما درچنین سیستم هایی یک DBMS مدیریت کننده نیز وجود دارد که مسئول برقراری هماهنگی بین سایر DBMS‌ها و نیز اعمال اینگونه قوانین جامعیتی سراسری می‌باشد.  
تراکنش‌های توزیع شده یک یا چند تراکنش جزئی تشکیل شده اند که ممکن است هریک از آنها مربوط به یکی از DBMS‌های سیستم باشد. چنین تراکنش هایی معمولا ابتدا توسط سیستم مدیریتی مرکزی دریافت می‌شوند و سپس هرکدام از پرس و جو‌های داخلی آن به DBMS مربوطه ارسال می‌گردد. اجرای هرکدام از پرس و جو‌های جزئی (که خود می‌توانند تراکنشی مستقل نیر باشند) بطور مستقل و محلی بر روی ماشین مربوطه اجرا شده و در انتها نیز نتیجه اجرا به سیستم مدیریتی باز گردانده می‌شود. سیستم مدیریتی مرکزی منتظر می‌ماند که تمامی تراکنش‌ها اعلام COMMIT کنند تا از انجام موفقیت آمیز همه انها اطمینان حاصل نماید. پس از کسب اطمینان کل تراکنش توسط این سیستم مرکزی COMMIT شده و در نتیجه تغییرات بر روی پایگاه داده توزیه شده اعمال می‌شوند. به این سیاست COMMIT کردن، کامیت دو مرحله ای یا Two-phase Commit گفته می‌شود. توجه داشته باشید که در صورتی که هریک از DBMS‌ها اعلام شکست نمایند تمامی تراکنش توزیع شده ROLLBACK می‌گردد.  
tx_begin();
            execute T1  //at site D
            execute T2  //at site C
            Execute T3  //at site B
            …
tX_commit ();
همانطور که در مثال بالا مشاهده می‌کنید تراکنش اصلی از سه تراکنش T1، T2 و T3 تشکیل شده که مر بوط به سه سایت متفاوت می‌باشند. در زمانی تراکنش اصلی COMMIT خواهد شد که هر سه سایت اعلام موفقیت کنند.
 
تراکنش‌های تو در تو (Nested Transaction):
این نوع از تراکنش نسبت به دو نوع تراکنش قبلی پیچیدگی بیشتری به لحاظ پیاده سازی و مدیریت دارند. این گونه تراکنش‌ها عموما واحد‌های کاری بزرگی هستند که در داخل آنها درختی از تراکنش‌های تو در تو را داریم که مجموعه تمامی انها در نهایت یک کار واحد بلحاظ منطقی را انجام می‌دهند. هر یک از تراکنش‌های داخلی بعنوان یک گره در این ساختار درختی قرار دارند که می‌توانند پدر و یا فرزندانی داشته باشند.
 
در تراکنش‌های تو در تو شرایطی حاکم است.
هر گره در ساختار درختی تراکنش تنها قادر به دیدن برادر‌های خود می‌باشد. به بیان دیگر فرزندان برادران خود را نمی‌بیند و نسبت به انها هیچ اطلاعی ندارد. 
در تراکنش‌های تو در تو امکان اجرای موازی فرزندان یک گره وجود دارد.
امکان اجرای موازی تراکنش‌ها منجر می‌شود به این که تراکنش‌های داخلی قادر به دیدن خروجی حاصل از اجرا همدیگر نباشند.
هر تراکنشی به طور مستقل ویژگی atomicity را دارد اما پایداری (durability) و کامیت شدن آنها وابسته به پدرانشان می‌باشد.
در صورتی که پدری تصمیم بگیرد می‌تواند تمامی زیر تراکنش هایش را خاتمه (abort) دهد.
در تراکنش‌های موازی COMMIT شدن یک گره پدر به دو صورت امکان پذیر است. 
 
حالت AND: در این حالت یک تراکنش در صورتی کامیت خواهد شده که تمامی فرزندان آن با موفقیت اجرا و COMMIT شده باشند.
حالت OR: در این حالت اگر حتی یکی از تراکنش‌های فرزند نیز موفق به COMMIT شده باشد تراکنش پدر نیز COMMIT خواهد شد.
 
تراکنش‌های چند سطحی (Multi-level Transactions) :
این نوع نیز همانند تراکنش‌های تو در تو پیچیده است. از نظر ساختاری تراکنش‌های چند سطحی مشابه تراکنش‌های تو در تو می‌باشند ولی به لحاظ مفهومی با یکدیگر متفاوت هستند. اولین تفاوت موجود بین این دو نوع اینست که هر زیر تراکنشی قادر است خروجی زیر تراکنش‌های دیگر را ببیند. این مسئله باعث می‌شود که تنوانیم زیر تراکنش‌ها را بصورت همروند و موازی اجرا کنیم که این دومین تفاوت مفهومی بین این دو می‌باشد. هنگامی که زیر تراکنش کامل شد (COMMIT) تمامی قفل‌های مربوط به خود را آزاد می‌کند که این مورد نیز در مورد تراکنش‌های تو در تو صادق نمی‌باشد. یکی از مهمترین تفاوت‌های دیگر بین این دو نوع در اینست که در تراکنش‌های چند سطحی تمامی برگ‌ها در یک سطح از درخت قرار دارند و تنها تراکنش‌های برگ هستند که مستقیما به پایگاه داده مراجعه می‌کنند. در مورد کایت شدن نیز شروط مربوط به تراکنش‌های تو در تو در اینجا وجود ندارند و زیر تراکنش‌ها می‌توانند بدون هیچ شرطی کامیت شوند.
 
تراکنش‌های زنجیره ای (Chained Transaction):
همانطور که از نام این نوع از تراکنش‌ها پیداست، این تراکنش‌ها از زنجیره ای از زیر تراکنش‌های پی در پی تشکیل شده اند. تا زمانی که تمامی حلقه‌های این زنجیر با موفقیت اجرا نشوند سیستم به حالت سازگاری نخواهد نرفت. دراین نوع از تراکنش‌های COMMIT هر حلقه باعث پایداری شدن (durable) داده‌های در پایگاه داده خواهد شد. این مسئله ممکن است پایگاه داده را به وضعیت ناسازگاری ببرد. در هنگام کامیت شدن هر حلقه قفل‌های مربوط به آن نیز آزاد می‌شود.
 
حلقه‌های مختلف زنجیره تراکنشی می‌توانند با یکدیگر تبادل اطلاعات کنند. البته توجه داشته باشید که منابعی که هر کدام از آنها بر روی آن کار می‌کنند با دیگری متفاوت می‌باشد. بعنوان نمونه تراکنشی را نظر بگیرد که قصد دارد متوسط مبلغ مکالمه تلفن همراه مشترکان یک مخابرات را محاسبه کند. بدلیل تعداد بالای مشترکان ممکن است این تراکنش را در قالب یک تراکنش زنجیره ای پیاده سازی کنیم که هر حلقه از آن مسئول محاسبه این مبلغ برای ده هزار نفر از کاربران باشد. توجه داشته باشید که برای بدست آوردن مقدار متوسط نیاز داریم که هر زیر تراکنش‌ها قادر به تبادل اطلاعات باشند. از طرفی منابع مورد استفاده آنها (رکورد ها) با یکدیگر متفاوت خواهد بود و نمی‌توانند تغییرات یکدیگر را ببینند. سوالی که مطرح می‌شود اینست که مبادله اطلاعات بین حلقه‌های تراکنش به چه صورت باید انجام شود؟ در جواب این سوال باید گفت که مبادله اطلاعات بین تراکنش‌ها از طریق متغیر‌های رابطه ای که هما متغیر‌های پایگاه داده هستند انجام می‌گیرد.
 
 
SavePoint:
در برخی شرایط ممکن است بخواهیم در هنگام ROLLBACK مجددا به ابتدای تراکنش باز نگردیم تا مجبور باشیم دوباره کار را از ابتدا از سر بگیریم. بعنوان مثال تا قسمتی از تراکنش پیش رفتیم، به خطایی بر خورد می‌کنیم و می‌خواهیم از نقطه ای خاص از تراکنش کا را از سر بگیریم. در چنین کاربرد هایی از ابزاری بنام SavePoint استفاده می‌کنم.
 
برای روشن‌تر شدن مفهوم SavePoint فرض کنید قصد داریم بلیطی از تهران به سیدنی رزرو کنیم. برای این منظور ابتدا عمل رزرواسیون را از تهران به دوبی انجام می‌دهیم و سپس از دوبی به سنگاپور و در نهایت از سنگاپور به سیدنی. حال در این بین می‌توانیم در نقطه تهران – دوبی SavePoint قرار دهیم تا در صورت بروز هرگونه خطا مجددا رزرواسیون را از ابتدا آغاز نکنیم. اگر در هنگام رزرو بلیط دوبی – سنگاپور خطایی بروز دهد می‌توانیم به نقطه تهران – دوبی ROLLBACK کنیم و از آنجا مسیر دیگری را انتخاب کنیم. توجه داشته باشید که ROLLBACK به SavePoint وضعیت پایگاه داده به همان نقطه بازگردانده می‌شود. 
begin transaction();
            s1;
            sp1:= create savepoint(0);
            s2;
            sp2:= create savepoint(0);
            if (condition)
            rollback (spi);
            …
            …
            commit
Auto Transaction:
این قبیل تراکنش‌ها تراکنش‌های کوچکی هستند  که توسط سیستم تعریف می‌شوند. بعنوان مثال سیستم برای انجام دستورات زیر تراکنش تعریف می‌کند :
Alter table, Create, delete, insert, open, drop, fetch, grant, revoke, select, truncate table, update
یکی از علت‌های این امر اینست که در صورت بروز خطا در حین این تراکنش‌های خود کار امکان اجرای مجدد هر کدام فراهم گردد.
 
شروع تراکنش‌ها :
همانطور که گفته شد برای شروع تراکنش‌ها می‌توانیم صراحتا از BEGIN TRANSACTION استفاده کنیم. البته راهکار دیگری نیز وجود دارد که در آن می‌توانیم به DBMS اعلام کنیم که با پایان یک تراکنش پیش از شروع تراکنش بعدی BEGIN TRANSACTION را قرار بده. برای این منظور از دستور زیر استفاده می‌کنیم :
Set implicit_transaction on
برخی از ویژگی‌های تراکنش‌ها را می‌توان تغییر داد. بعنوان مثال می‌توان گفت که تراکنش جاری تنها اجازه خواندن از پایگاه داده را دارد. در این حالت از دستور زیر می‌توان استفاده نمود : 
SET TRANSACTION READ ONLY
همچنین میتوان اجازه تغییر را  به آن داد :
SET TRANSACTION READ WRITE
علاوه بر موارد بالا می‌توان سطح ایزولاسیون تراکنش را با دستود SET تغییر داد. این سطوح در زیر آورده شده اند که بحث در مورد آنها را به مقاله دیگر در مقوله همروندی موکول می‌کنیم. 
READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE
موفق و پیروز باشید
مطالب
کار با بانک‌های اطلاعاتی مختلف در PdfReport
تعدادی از منابع داده پیش فرض PdfReport جهت کار مستقیم با بانک‌های اطلاعاتی مختلف، کوئری نوشتن و نمایش نتایج آن‌ها طراحی شده‌اند.
در این بین با توجه به اینکه دات نت پشتیبانی توکاری از SQL Server دارد، اتصال و استفاده از توانمندی‌های آن نیاز به کتابخانه جانبی خاصی ندارد. اما برای کار با بانک‌های اطلاعاتی دیگر نیاز خواهد بود تا پروایدر ADO.NET آن‌ها را تهیه و به برنامه اضافه کنیم.
چهار نمونه از منابع داده پیش فرضی که در متد MainTableDataSource قابل تعریف هستند به شرح زیر می‌باشند:
public void SqlDataReader(string connectionString, string sql, params object[] parametersValues)

//.mdb or .accdb files
public void AccessDataReader(string filePath, string password, string sql, params object[] parametersValues)

public void OdbcDataReader(string connectionString, string sql, params object[] parametersValues)
SqlDataReader برای کار با بانک‌های اطلاعاتی SQL Server بهینه سازی شده است.
AccessDataReader قابلیت اتصال به بانک‌های اطلاعاتی اکسس جدید (فایل‌های accdb) و اکسس قدیم (فایل‌های mdb) را دارد.
OdbcDataReader یک پروایدر عمومی است که از روز اول دات نت به همراه آن بوده است. برای مثال جهت اتصال به بانک‌های اطلاعاتی فاکس‌پرو می‌تواند مورد استفاده قرار گیرد.
اما ... برای مابقی بانک‌های اطلاعاتی چطور؟
برای سایر بانک‌های اطلاعاتی، منبع داده عمومی زیر تدارک دیده شده است:
public void GenericDataReader(string providerName, string connectionString, string sql, params object[] parametersValues)
تنها تفاوت آن با نمونه‌های قبل، ذکر providerName آن است. برای مثال جهت اتصال به SQLite ابتدا پروایدر مخصوص ADO.NET آن‌را دریافت و به پروژه خود اضافه نمائید. سپس پارامتر providerName فوق را با "System.Data.SQLite" مقدار دهی کنید.

یک نکته:
در تمام منابع داده فوق، امکان نوشتن کوئری‌های پارامتری نیز پیش بینی شده است. فقط باید دقت داشت که پارامترهای معرفی شده باید با @ شروع شوند که یک نمونه از آن‌را در مثال جاری ملاحظه خواهید نمود.

در ادامه نحوه تهیه گزارش از یک بانک اطلاعاتی SQLite را توسط PdfReport بررسی خواهیم کرد:

using System;
using PdfRpt.Core.Contracts;
using PdfRpt.Core.Helper;
using PdfRpt.FluentInterface;

namespace PdfReportSamples.SQLiteDataReader
{
    public class SQLiteDataReaderPdfReport
    {
        public IPdfReportData CreatePdfReport()
        {
            return new PdfReport().DocumentPreferences(doc =>
            {
                doc.RunDirection(PdfRunDirection.RightToLeft);
                doc.Orientation(PageOrientation.Portrait);
                doc.PageSize(PdfPageSize.A4);
                doc.DocumentMetadata(new DocumentMetadata { Author = "Vahid", Application = "PdfRpt", Keywords = "Test", Subject = "Test Rpt", Title = "Test" });
            })
            .DefaultFonts(fonts =>
            {
                fonts.Path(AppPath.ApplicationPath + "\\fonts\\irsans.ttf",
                                  Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\verdana.ttf");
            })
            .PagesFooter(footer =>
            {
                footer.DefaultFooter(DateTime.Now.ToString("MM/dd/yyyy"));
            })
            .PagesHeader(header =>
            {
                header.DefaultHeader(defaultHeader =>
                {
                    defaultHeader.RunDirection(PdfRunDirection.RightToLeft);
                    defaultHeader.ImagePath(AppPath.ApplicationPath + "\\Images\\01.png");
                    defaultHeader.Message("گزارش جدید ما");
                });
            })
            .MainTableTemplate(template =>
            {
                template.BasicTemplate(BasicTemplate.SilverTemplate);
            })
            .MainTablePreferences(table =>
            {
                table.ColumnsWidthsType(TableColumnWidthType.Relative);
                table.NumberOfDataRowsPerPage(5);
            })
            .MainTableDataSource(dataSource =>
            {
                dataSource.GenericDataReader(
                    providerName: "System.Data.SQLite",
                    connectionString: "Data Source=" + AppPath.ApplicationPath + "\\data\\blogs.sqlite",
                    sql: @"SELECT [url], [name], [NumberOfPosts], [AddDate]
                               FROM [tblBlogs]
                               WHERE [NumberOfPosts]>=@p1",
                    parametersValues: new object[] { 10 }
                );
            })
            .MainTableSummarySettings(summarySettings =>
            {
                summarySettings.OverallSummarySettings("جمع کل");
                summarySettings.PreviousPageSummarySettings("نقل از صفحه قبل");
                summarySettings.PageSummarySettings("جمع صفحه");
            })
            .MainTableColumns(columns =>
            {
                columns.AddColumn(column =>
                {
                    column.PropertyName("rowNo");
                    column.IsRowNumber(true);
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(0);
                    column.Width(1);
                    column.HeaderCell("ردیف");
                });

                columns.AddColumn(column =>
                {
                    column.PropertyName("url");
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(1);
                    column.Width(2);
                    column.HeaderCell("آدرس");
                    column.ColumnItemsTemplate(template =>
                    {
                        template.Hyperlink(foreColor: System.Drawing.Color.Blue, fontUnderline: true);
                    });
                });

                columns.AddColumn(column =>
                {
                    column.PropertyName("name");
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(2);
                    column.Width(2);
                    column.HeaderCell("نام");
                });

                columns.AddColumn(column =>
                {
                    column.PropertyName("NumberOfPosts");
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(3);
                    column.Width(2);
                    column.HeaderCell("تعداد مطلب");
                    column.ColumnItemsTemplate(template =>
                    {
                        template.TextBlock();
                        template.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj));
                    });
                    column.AggregateFunction(aggregateFunction =>
                    {
                        aggregateFunction.NumericAggregateFunction(AggregateFunction.Sum);
                        aggregateFunction.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj));
                    });
                });

                columns.AddColumn(column =>
                {
                    column.PropertyName("AddDate");
                    column.CellsHorizontalAlignment(HorizontalAlignment.Center);
                    column.IsVisible(true);
                    column.Order(4);
                    column.Width(2);
                    column.HeaderCell("تاریخ ثبت");
                    column.ColumnItemsTemplate(template =>
                    {
                        template.TextBlock();
                        template.DisplayFormatFormula(obj => obj == null ? string.Empty : PersianDate.ToPersianDateTime((DateTime)obj) /*((DateTime)obj).ToString("dd/MM/yyyy HH:mm")*/);
                    });
                });
            })
            .MainTableEvents(events =>
            {
                events.DataSourceIsEmpty(message: "There is no data available to display.");
            })
            .Export(export =>
            {
                export.ToExcel();
            })
            .Generate(data => data.AsPdfFile(AppPath.ApplicationPath + "\\Pdf\\RptSqlDataReaderSample.pdf"));
        }
    }
}

توضیحات:

- در مثال فوق نحوه استفاده از یک بانک اطلاعاتی SQLite را ملاحظه می‌کنید. این بانک اطلاعاتی نمونه در پوشه bin\data سورس به روز شده پروژه موجود است.
                dataSource.GenericDataReader(
                    providerName: "System.Data.SQLite",
                    connectionString: "Data Source=" + AppPath.ApplicationPath + "\\data\\blogs.sqlite",
                    sql: @"SELECT [url], [name], [NumberOfPosts], [AddDate]
                               FROM [tblBlogs]
                               WHERE [NumberOfPosts]>=@p1",
                    parametersValues: new object[] { 10 }
                );
فرض بر این است که فایل‌های System.Data.SQLite.dll و SQLite.Interop.dll را از سایت SQLite دریافت کرده و سپس ارجاعی را به اسمبلی System.Data.SQLite.dll به پروژه خود افزوده‌اید.
در مرحله بعد به کمک GenericDataReader می‌توان به این پروایدر دسترسی یافت. همانطور که ملاحظه می‌کنید یک کوئری پارامتری با مقدار پارامتر مساوی 10 جهت تهیه گزارش، تعریف شده است.
همچنین باید دقت داشت که اگر پروژه جاری شما مبتنی بر دات نت 4 است، نیاز خواهید داشت چند سطر زیر را به فایل config برنامه اضافه نمائید تا با SQLite مشکلی نداشته باشد:
<?xml version="1.0"?>
   <configuration>
      <startup useLegacyV2RuntimeActivationPolicy="true">
           <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
      </startup>
   </configuration>                  
- مرحله بعد نوبت به معرفی ستون‌های گزارش است. هر ستون، معادل یک فیلد معرفی شده در کوئری SQL ارسال شده به GenericDataReader خواهد بود (کوچکی و بزرگی حروف باید در اینجا رعایت شوند).
- در حین معرفی ستون AddDate، نحوه نمایش و تبدیل تاریخ دریافتی که با فرمت DateTime است را به تاریخ شمسی ملاحظه می‌کنید. متد PersianDate.ToPersianDateTime در فضای نام PdfRpt.Core.Helper قرار دارد. توسط DisplayFormatFormula، فرصت خواهید داشت مقدار متناظر با سلول در حال رندر را پیش از نمایش، به هر نحو دلخواهی فرمت کنید.
- در ستون url از قالب نمایشی پیش فرض Hyperlink، برای نمایش اطلاعات فیلد جاری به صورت یک لینک قابل کلیک استفاده شده است.

یک نکته:
ذکر قسمت MainTableColumns و تمام تعاریف مرتبط با آن در PdfReports اختیاری است. به این معنا که می‌توانید قسمت گزارش سازی و تعاریف گزارشات برنامه خود را پویا کنید (شبیه به حالت auto generate columns در گرید‌های معروف). کوئری‌های SQL متناظر با گزارشات را در بانک اطلاعاتی ذخیره کنید و به گزارش ساز فوق ارسال نمائید. حاصل یک گزارش جدید است.

نظرات مطالب
بررسی روش دسترسی به HttpContext در ASP.NET Core
برای دسترسی به رویدادهای Session_Start و Session_End و دسترسی به متغیر Application که قبلا در Global.asax قرار داشتند .چه روشی وجود دارد؟ برای مثال: بدست آوردن Session‌های Active و قرار دادن آن در متغیری که در کل برنامه در دسترس باشد.برای شمارش تعداد ویزیتورها و ...
نظرات مطالب
صفحه بندی و مرتب سازی خودکار اطلاعات به کمک jqGrid در ASP.NET MVC
مطالب گروه jqGrid را بررسی کنید. الزامی به استفاده از فرم‌های پیش فرض آن نیست. امکان inline add/edit هم دارد به همراه سفارشی سازی جزئیات آن‌ها. در مورد رویدادهای آن هم در لابلای مباحث این گروه با ارائه کدهای مرتبط، بحث شده‌است.
نظرات مطالب
افزودن هدرهای Content Security Policy به برنامه‌های ASP.NET
- به ASP.NET MVC کوچ کنید تا کنترل کاملی بر روی عناصر اضافه شونده به صفحه داشته باشید.
- با استفاده از jQuery به هر عنصری در صفحه می‌توان رویدادهای خاصی را انتساب داد یا حذف کرد. انجام اینکار از طریق یک فایل js الحاق شده به صفحه نیز میسر است.
بازخوردهای دوره
معرفی پروژه NotifyPropertyWeaver
سلام؛
چطور یک Subscriber برای رویداد propertyChanged بنویسیم، درصورتیکه این رویداد وجود ندارد؟
درصورت امکان، یک مثال از نحوه اطلاع رسانی هنگام تغییر مقدار Property بزنید.
ممنون
نظرات مطالب
ASP.NET MVC #13
سلام مهندس نصیری
در قسمت remote validation زمانی که رویداد blur مربوط به کنترل مورد نظر اتفاق افتاد یک درخواست به صورت  ajax ارسال می شود آیا امکان تغییر رویداد مورد نظر امکان پدیر می باشد
مطالب
کامپوننت‌های متداول طرحبندی صفحات در بوت استرپ 4
بوت استرپ، به همراه کامپوننت‌هایی برای پیاده سازی اعمال متداول طرحبندی صفحات است؛ مانند jumbotron ،media ،table و card.


کامپوننت jumbotron

از Jumbotron برای نمایش متنی مشخص در بالای یک صفحه، استفاده می‌شود. دو روش استفاده‌ی از آن در بوت استرپ 4 وجود دارند:
- داخل container:
    <div class="container">
        <header class="jumbotron mt-4">
            <div class="display-2 mb-4">Our Mission</div>
            <p class="lead">Wisdom Pet Medicine strives to blend the best in
                traditional and alternative medicine in the diagnosis and
                treatment of companion animals including dogs, cats, birds,
                reptiles, rodents, and fish. We apply the wisdom garnered in
                the centuries old tradition of veterinary medicine, to find the
                safest treatments and cures.</p>
        </header>
با این خروجی:


در اینجا با اعمال کلاس jumbotron، متن header، داخل یک قاب با گوشه‌های گرد قرار می‌گیرد و مشخص‌تر نمایش داده خواهد شد. همچنین با mt-4، فاصله‌ای را بین آن و بالای صفحه ایجاد کرده‌ایم.

- خارج از container:
    <header class="jumbotron jumbotron-fluid">
        <div class="container">
            <div class="display-2 mb-4">Our Mission</div>
            <p class="lead">Wisdom Pet Medicine strives to blend the best in
                traditional and alternative medicine in the diagnosis and
                treatment of companion animals including dogs, cats, birds,
                reptiles, rodents, and fish. We apply the wisdom garnered in
                the centuries old tradition of veterinary medicine, to find the
                safest treatments and cures.</p>
        </div>
    </header>
با این خروجی:


اگر می‌خواهیم این قاب، تمام عرض صفحه را پر کند و همچمنین لبه‌های گرد آن نیز حذف شوند، می‌توان از کلاس jumbotron-fluid استفاده کرد و آن‌را خارج از container قرار داد. سپس برای اینکه متن داخل آن با container زیر آن تراز شود، می‌توان یک container را در اینجا داخل jumbotron تعریف کرد.


کنترل ظاهر جداول، در بوت استرپ 4

بوت استرپ 4 به همراه تعدادی کلاس ویژه است که برای بهبود ظاهر المان استاندارد جدول، ارائه شده‌اند. آن‌ها را در طی مثال‌هایی بررسی خواهیم کرد.


برای رسیدن به چنین تصویری، تغییرات زیر را بر روی یک جدول استاندارد HTML اعمال کرده‌ایم:
<table class="table table-striped table-hover table-bordered table-responsive">
  <thead class="thead-light">
- کلاس table، کلاس پایه اعمال شیوه‌نامه‌های بوت استرپ 4 به المان جدول است که سبب خواهد شد آیتم‌های آن با فاصله‌ی بهتری نسبت به یکدیگر ظاهر شوند. با استفاده از کلاس table-dark می‌توان یک قالب مشکی را به جدول اعمال کرد.
- کلاس table-striped سبب می‌شود تا ردیف‌ها، یک در میان با رنگی متمایز نمایش داده شوند.
- با افزودن table-hover، رنگ ردیف‌های جدول با عبور اشاره‌گر ماوس از روی آن‌ها تغییر می‌کند.
- کلاس table-bordered کار نمایش قاب جدول را انجام می‌دهد.
- کلاس table-responsive سبب می‌شود تا در اندازه‌های کوچک صفحه، یک اسکرول بار افقی برای نمایش آیتم‌های جدول ظاهر شود و یا می‌توان از کلاس table-sm نیز استفاده کرد تا padding تعریف شده‌ی در جدول، کاهش یابند. این کلاس، قابلیت پذیرش break-pointها را نیز دارد؛ مانند table-responsive-md.
- کلاس‌های thead-light و یا thead-dark که بر روی تگ thead قرار می‌گیرند، رنگ پس زمینه‌ی هدر جدول را مشخص می‌کنند.
- برای تغییر رنگ پس زمینه و متن یک ردیف می‌توان از کلاس‌های bg-color و text-color استفاده کرد:
<tr class="bg-danger text-light">
- برای تغییر رنگ سلول‌های جدول از کلاس‌های table-color استفاده می‌کنیم:
<td class="table-success">$100.00 </td>
فرمول‌های رنگ‌های قابل اعمال به ردیف‌ها، سلول‌ها و متون جداول بوت استرپ 4 را در تصویر ذیل مشاهده می‌کنید:



کامپوننت جدید card در بوت استرپ 4

پنل‌های بوت استرپ 3 حذف و بجای آن کامپوننت جدیدی به نام card در نگارش 4 آن ارائه شده‌است که با افزودن کلاس آن به یک div، بلافاصله قابی با گوشه‌های گرد به آن اضافه می‌شود.


        <section class="card mb-5" id="drwinthrop">
            <div class="card-body">
                <img class="card-img img-fluid" src="images/testimonial-mcphersons.jpg"
                    alt="Doctor Winthrop Photo">
                <h2 class="card-title">Dr. Stanley Winthrop</h2>
                <h5 class="card-subtitle">Behaviorist</h5>
                <p class="card-text">Dr. Winthrop is the guardian of Missy, a
                    three-year old Llaso mix, who he adopted at the shelter.
                    Dr. Winthrop is passionate about spay and neuter and pet
                    adoption, and works tireless hours outside the clinic,
                    performing free spay and neuter surgeries for the shelter.</p>
                <a class="card-link" href="#">About Me</a>
                <a class="card-link" href="#">My Pets</a>
                <a class="card-link" href="#">Client Slideshow</a>
            </div>
        </section>
- برای اینکه عناصر داخل card با فاصله‌ی مناسبی از لبه‌های آن قرار گیرند و همچنین شیوه‌نامه‌های قسمت‌های مختلف آن به درستی اعمال شوند، نیاز است محتوای section ای که با کلاس card مشخص شده (تعیین container)، داخل یک div با کلاس card-body قرار گیرد. در اینجا امکان تعریف card-header و card-footer نیز وجود دارد.
- سپس یک card می‌تواند دارای تصویری واکنشگرا باشد که عرض card را پوشش می‌دهد. این تصویر با کلاس card-img مشخص می‌شود.
در اینجا امکان تعریف card-img-top و card-img-bottom نیز وجود دارند. این موارد تصویر card را در بالا و یا پایین آن، بدون padding، نمایش می‌دهند. اگر می‌خواهید متنی را بر روی این تصویر نمایش دهید، از کلاس card-img-overlay استفاده کنید. در این حالت‌ها باید تصویر را خارج از card-body قرار دهید.
- عنوان و زیرعنوان یک card، توسط کلاس‌های card-title و card-subtitle تعیین می‌شوند.
- متن داخل آن‌را با کلاس card-text مشخص می‌کنیم.
- لینک‌های ذیل آن نیز توسط کلاس card-link در طی یک ردیف نمایش داده می‌شوند.

امکان تعیین رنگ پس زمینه، حاشیه و متن یک card نیز وجود دارند:
<section class="card mb-5 bg-primary text-light border-warning" id="drchase">
با این خروجی:


و فرمول کلی رنگ‌های آن نیز به صورت زیر می‌باشد:


می‌توان برای یک card، هدر و فوتر نیز تعریف کرد:
        <section class="card mb-5" id="drsanders">
            <div class="card-header">
                <h2 class="card-title">Dr. Kenneth Sanders</h2>
                <h5 class="card-subtitle">Nutritionist</h5>
            </div>
            <div class="card-body">
                <img class="card-img img-fluid" src="images/testimonial-mcphersons.jpg"
                    alt="Doctor Sanders Photo">
                <p class="card-text">Leroy walked into Dr. Sanders front door
                    when she was moving into a new house. After searching for
                    weeks for Leroy's guardians, she decided to make Leroy a
                    part of her pet family, and now has three cats.</p>
            </div>
            <div class="card-footer">
                <a class="card-link" href="#">About Me</a>
                <a class="card-link" href="#">My Pets</a>
                <a class="card-link" href="#">Client Slideshow</a>
            </div>
        </section>
در اینجا همان card قبلی را مشاهده می‌کنید که عناوین آن به card-header و لینک‌های ذیل آن به card-footer منتقل شده‌اند:


برای تعریف یک list-group در داخل یک card، به صورت زیر عمل می‌کنیم:
        <section class="card mb-5" id="drwong">
            <div class="card-body">
                <img class="card-img img-fluid" src="images/testimonial-mcphersons.jpg"
                    alt="Doctor Wong Photo">
                <h2 class="card-title">Dr. Olivia Wong</h2>
                <h5 class="card-subtitle">Preventive Care</h5>
                <p class="card-text">Dr. Wong is a cancer survivor who was
                    fortunate enough to get to spend time with a therapy dog
                    during her recovery. She became passionate about therapy
                    animals, and has started her own foundation to train and
                    provide education to patients in recovery. Now she gets her
                    own dose of daily therapy from her husky, Lilla.</p>
            </div>
            <div class="list-group list-group-flush">
                <a class="list-group-item" href="#">About Me</a>
                <a class="list-group-item" href="#">My Pets</a>
                <a class="list-group-item" href="#">
                    Client Slideshow
                </a>
            </div>
        </section>
ابتدا list-group را به خارج از card-body منتقل می‌کنیم. سپس برای حذف حاشیه‌ی آن و همچنین گوشه‌های گرد آن، جهت یکی شدن با قاب card، کلاس list-group-flush را به آن اضافه می‌کنیم:



تعیین نحوه‌ی چیدمان cards در بوت استرپ 4

اگر چندین card در یک صفحه تعریف شده‌اند، برای تعیین نحوه‌ی قرارگیری آن‌ها در کنار یکدیگر می‌توان یا از سیستم طرحبندی متداول بوت استرپ استفاده کرده و یا امکان تعریف گروهی از آن‌ها نیز وجود دارد. برای اینکار کافی است یک div با کلاس card-group را تعریف و سپس تمام cards را داخل آن قرار دهیم:
    <div class="container">
        <div class="card-group">
که سبب خواهد شد تمام cards در کنار یکدیگر بدون فاصله‌ای نمایش داده شوند. اگر بجای آن از کلاس card-deck استفاده شود، فاصله‌ای بین cards قرار می‌گیرد.


اگر از کلاس card-columns استفاده کنیم، تمام cards را به صورت خودکار در ستون‌ها و ردیف‌ها، قرار می‌دهد که بعضی از آن‌ها بلندتر و بعضی دیگر کوتاه‌تر هستند (نوعی نمایش کاشی‌کاری شده‌است):


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


المان media در بوت استرپ 4

برای نمایش متداول متن و تصویر که قرار است تصویر، در یک ستون و متن، در ستونی دیگر باشد، بوت استرپ 4 به همراه کلاس media است که بر اساس Flexbox بازنویسی شده‌است.
<body>
    <div class="container">
        <section class="media mb-5" id="drwinthrop">
            <img class="d-flex align-self-center img-fluid rounded mr-3" style="width:30%"
                src="images/testimonial-mcphersons.jpg" alt="Doctor Winthrop Photo">
            <div class="media-body">
                <h2>Dr. Stanley Winthrop</h2>
                <h5>Behaviorist</h5>
                <p>Dr. Winthrop is the guardian of Missy,
                    a three-year old Llaso mix, who he adopted at the
                    shelter. Dr. Winthrop is passionate about spay and neuter
                    and pet adoption, and works tireless hours outside the
                    clinic, performing free spay and neuter surgeries for the
                    shelter.</p>
        </section>
    </div>
</body>
با این خروجی:


ابتدا توسط کلاس media یک container را تعریف می‌کنیم. سپس تصویر، یک ستون و media-body ستون دیگر را تشکیل می‌دهد.
با استفاده از d-flex، المان تصویر را به یک Flexbox container تبدیل کرده و با استفاده از کلاس align-self-center، آن‌را در میانه‌ی ستون قرار می‌دهیم. همچنین در اینجا توسط mr-3، فاصله‌ی آن‌را با متن ستون کناری تنظیم کرده‌ایم.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: Bootstrap4_09.zip