جلوگیری از ورود اطلاعات نا معتبر در Database از طریق Sql Transaction (بخش دوم)
گروه: ASP.NET Visual Basic Database / LINQ / EF
تاریخ ثبت: ۸۶/۴/۱۱
نویسنده: ایمان شبانی

مقدمه :

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

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

 

 روش سوم – عمل مدیریت اطلاعات را با امکانات بیشتر و البته پیچیدگی بیشتری انجام دهید :

            کلاس: SqlTransaction

            مشخصه ی بارز: پر امکانات ترین روش

 پیاده سازی کلی این روش (بدون امکانات ) تا حد زیادی مشابه پیاده سازی روش دوم است.

 کلاس SqlTransaction در فضای نامی System.Data.SqlClient قرار دارد، و با توجه به اینکه در هر برنامه ی پایگاه داده، این کلاس قبلا  Imports ( یا در C# ،  Using) شده است، شما نیاز به افزودن هیچ فضای نامی به برنامه نخواهید داشت.

برای پیاده سازی این روش، ابتدا نیاز به یک شی از این کلاس دارید:

 

Dim sqlTransaction1 As SqlTransaction

 

نکته: دقت داشته باشید که کلاس SqlTransaction دارای سازنده نیست، پس نباید از کلمه ی کلیدی New در تعریف شی استفاده کنید.

همچنین باید بلافاصله بعد از باز کردن ارتباط، متد BeginTransaction از شی sqlConnection1 را به شی SqlTransaction انتساب دهید:

 

sqlTransaction1 = sqlConnection1.BeginTransaction()

 

سپس باید شی SqlTransaction را به خصیصه ی Transaction از شی sqlConnection1 انتساب دهید:

 

sqlCommand1.Transaction = sqlTransaction1

 

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

 

sqlTransaction1.Rollback()

 

و در صورت اجرای موفقیت آمیز دستورات، لازم است متد Commit از شی را ( برای اعمال تغییرات در Database ) فراخوانی کنید:

 

sqlTransaction1.Commit()

 

همان طور که قبلا هم گفته شد، جایگذاری این دستورات توسط شما و طبق صلاحدید شما انجام می پذیرد، ولی به عنوان یک استاندارد، فراخوانی متد Commit بلافاصله قبل از بستن ارتباط، و فراخوانی متد Rollback در قسمت Catch شده ( در ساختار Try دوم ) قرار می گیرد.

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

 مثال: کد زیر سابروتینی را نشان می دهد، که یک رکورد به جدول اضافه می کند. در این سابروتین از کلاس SqlTransaction برای عمل Transaction استفاده شده است:

نکته: در اینجا ناگزیرید از دو ساختار Try... Catch... End Try تو در تو استفاده کنید، به این دلیل که شی SqlTransaction به دو انتساب نیاز دارد، و در صورتی که در ساختار Try اول، این انتسابها را انجام دهید، نمی توانید در Catch همان ساختار، دستور sqlTransaction1.Rollback() را بنویسید، چون طول عمر انتساب ها قبل از  Catch به اتمام می رسد.

در این سابروتین، برنامه ابتدا یک ارجاع به شی SqlTransaction ایجاد می کند،  و بعد از باز کردن ارتباط، شروع Transaction در شی sqlConnection1 را به شی SqlTransaction انتساب داده و همین طور شی SqlTransaction را به متد Transaction از شی sqlCommand1 انتساب می دهد. سپس عملیات درج در جدول را انجام می دهد، در پایان در صورتی که خطایی رخ ندهد، متد Commit فراخوانی شده و تغییرات در Database ثبت می شوند و اجرای برنامه پس از درج، با موفقیت به پایان می رسد. در غیر این صورت، اگر در ساختار Try... Catch... End Try  دوم خطایی رخ دهد، اجرای برنامه، قبل از فراخوانی متد Commit از شی SqlTransaction ، به داخل Catch منتقل می شود و در نتیجه متد Commit فراخوانی نشده و در عوض متد Rollback فراخوانی می شود. این متد ( برخلاف روش اول )، صراحتا تمامی تغییرات Database ( از ارسال شی SqlTransaction به ارتباط، تا اینجا ) نادیده گرفته می شود و Database به حالتی که قبل از اجرای سابروتین AddToTable داشته، باز می گردد.

 یک سوال: در اینجا یک سوال پیش می آید و آن اینکه چرا این کلاس ایجاد شده، در صورتی که همین اعمال با روش دوم قابل انجام هستند، آنهم با دستورات و پیچیدگی به مراتب کمتر ؟

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

برای اینکه بهتر متوجه منظورم بشوید، به سرویس Restore Point در Windows XP ( و بالاتر ) نگاهی بیاندازید. رابط کاربری این سرویس را می توانید به صورت زیر مشاهده کنید: در پنجره ی Run ویندوز، عبارت restore را تایپ کرده و بر روی دکمه ی OK کلیک کنید. در صفحه ی باز شده، فایل restrui.exe را اجراکنید. در این لحظه، صفحه ی خوش آمدگویی System Restore ظاهر می شود. در سمت راست این صفحه دو گزینه وجود دارد. یکی Create Restore Point که توسط آن می توانید یک نقطه ی بازگشت به صورت دستی برای ویندوز ایجاد کنید، و دیگری Restore My Computer To An Earlier Time که تنظیمات و فایل های اصلی ویندوز را (تا حدی که برایش ظرفیت وجود دارد ) به یک زمان زوتر بر می گرداند.

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

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

 برای اینکار به جای دستور

 

sqlTransaction1 = sqlConnection1.BeginTransaction()

 

از دستور زیر استفاده می کنیم:

 

 sqlTransaction1 = sqlConnection1.BeginTransaction("First")

 

"First" در دستور بالا صرفا یک نام برای نقطه ی بازگشت است و هر نامی برای آن می توان انتخاب کرد. در این حالت، هم می توانید از دستور ()sqlTransaction1.Rollback استفاده کنید که به اولین نقطه ی پردازش نشده باز می گردد، و هم در صورتی که چند نقطه ی بازگشت داشته باشید، می توانید از دستور زیر استفاده کنید:

 

 sqlTransaction1.Rollback("Second")

 

کلاس SqlTransaction همچنین دازای یک متد به نام Save است که به عنوان پارامتر، نام یک نقطه ی بازگشت را می گیرد، و تغییرات تا آن لحظه را در Database ذخیره می کند. کاربرد آن زمانی است که می خواهید قبل از پایان عملیات و فراخوانی متد Commit از شی، مقداری از اطلاعات را در Database تثبیت کنید:

 

 sqlTransaction1.Save("First")

 

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

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

نکته: روشهای دیگری هم برای عمل Transaction در .NET وجود دارند، از جمله استفاده از کلاس DependentTransaction ، که در برنامه های Multi Thread کاربرد دارد.

نکته: روش اول در برخی از سیستمها به دلایل نامعلومی ناقص عمل کرده و تمامی اطلاعات را Rollback می کند. برای حل این مشکل، تعریف یک شی از TransactionScop را، به جای ابتدای سابروتین، بلافاصله قبل از باز کردن ارتباط انجام دهید.

یک سوال: حال این سوال پیش می آید که بالاخره کدام روش را انتخاب کنیم ؟

 پاسخ: برای پاسخ به این سوال لازم است  روش ها با هم مقایسه شده، و معایب و مزایای هر کدام بررسی شود:

روش اول: این روش ساده ترین و سریعترین روش ممکن است. اگر در برنامه تان حالت خاصی وجود ندارد که بخواهید به صورت دستی آن را مدیریت کنید، این روش توصیه می شود، چون از آنجا که مدیریت آن به عهده ی خود NET. بوده، احتمال بروز خطا در آن بسیار کم است. ولی در این روش نمی توانید به صورت دستی آن را برنامه راکنترل کنید، البته راهی وجود دارد، و آن ایجاد عمدی یک استثناست. زیرا در این روش، موفقیت در احرا و یا بروز یک مشکل، تنها با استثنا ها بررسی می شود و این یعنی اینکه شما می توانید برای جایگزینی متد Rollback در این روش، با استفاده از دستور Throw یک استثناء به برنامه بفرستید، در این صورت متد Complete اجرا نخواهد شد و نتیجه مشابه فراخوانی متد Rollback در روش های دوم و سوم است. با این همه به دلیل حفظ نظم در ساختار برنامه، توصیه می شود برای کنترل دستی از روش های دوم و سوم استفاده کنید.

( اگر این روش را به عنوان روش اصلی انتخاب کنید، هنوز در کمتر از 5% مواقع (شاید اصلا برایتان پیش نیاید ) به روش سوم و در حدود 20% مواقع به روش دوم نیاز خواهید داشت. )

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

( اگر این روش را به عنوان روش اصلی انتخاب کنید، هنوز در کمتر از 5% مواقع (شاید اصلا برایتان پیش نیاید ) به روش سوم نیاز خواهید داشت. )

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

 ( اگر این روش را به عنوان روش اصلی انتخاب کنید، در 99% مواقع جوابگوی نیاز شما خواهد بود. )

 

پروژه ی نمونه :

در پروژه نمونه ی ایجاد شده، عمل درج یکبار بدون Transaction و یک بار هم با هر کدام از روشها انجام شده است:

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

  سخن پایانی :

 ·          از اینکه تا اینجا با من همراه بودید، کمال تشکر را دارم.

·     بحث در مورد Sql Transaction به این سه روش ختم نمی شود، بلکه هم روش های دیگری وجود دارند و هم هر روش جزئیات وسیعی را شامل می شود. با این همه به ارائه ی این سه روش اکتفا می کنم،  بقیه ی مطالب را ( در صورتی که به آن احساس علاقه می کنید ) می توانید در MSDN دنبال کنید، هر چند در صورت ایجاد هر گونه ابهام و یا سوالی در مورد سایر روش ها، بنده در حد توان در خدمت دوستان هستم.

·          امیدوارم مرا از نظرات ، پیشنهادات و انتقادات خود مطلع کنید.

فایل Solution پروژه را از اینجا دریافت کنید.

 


Copyright © 2006 - 2011 All Rights Reserved.
Please direct your questions or comments to