اندازهی قلم متن
تخمین مدت زمان مطالعهی مطلب:
پنج دقیقه
TransactionScope روشی برای پیاده سازی تراکنش در .Net است که برای اولین بار در دات نت 2 معرفی شده است. روش پیاده سازی آن بسیار ساده است و همین سادگی و راحتی کار با اون باعث شده است که خیلی از برنامه نویسها رو متمایل به خودش کنه. در ادامه به روش استفاده و مزایا و معایب این روش برای پیاده سازی تراکنشها میپردازیم.
این روش دارای تمام خواص یک تراکنش است(اصطلاحا به این خواص ACID Properties گفته میشود)
1-Atomic : به این معناست که تمام دستورات بین بلاک (دستورات SQL و سایر عملیات) باید به صورت عملیات اتمی کار کنند. یعنی یا تمام عملیات موفقیت آمیز است یا همه با شکست روبرو میشوند.
2 - Consistent: به این معناست که اگر تراکنش موفقیت آمیز بود پایگاه داده باید در شروع تراکنش بعدی تغییرات لازم رو انجام داده باشد و در غیر این صورت پایگاه داده باید به حالت قبل از شروع تراکنش برگردد.
3- Isolated: اگر چند تا تراکنش هم زمان شروع شوند اجرای هیچ کدوم از اونها نباید بر اجرای بقیه تاثیر بزاره.
4- Durable: یعنی تغییرات حاصل شده بعد از اتمام تراکنش باید دائمی باشند.
روش کار به این صورت است تمام کارهایی که قصد داریم در طی یک تراکنش انجام شوند باید در یک بلاک قرار بگیرند و تا زمانی که متد Complete فراخوانی شود. در این بلاک شما هر عملیاتی رو که به عنوان جزئی از تراکنش میدونید قرار بدید. در صورتی که کنترل اجرا به فراخوانی دستور Complete برسه تمام موارد قبل از این دستور Commit میشوند در غیر این صورت RollBack.
به مثال زیر دقت کنید.
ابتدا به پروژه مربوطه باید اسمبلی System.Transaction رو اضافه کنید.
تمام دستوراتی که در این بلاک نوشته شوند بعد از فراخوانی دستور scope.Complete اصطلاحا Commit میشوند. اگر به هر دلیلی فراخوانی دستورات به scope.Complete نرسد عمل RollBack انجام میشود. در نتیجه برای این که عمل RollBack رو انجام دهید بهتره که قبل از دستور Complete یک Exception رو پرتاب کنید که باعث فراخوانی Dispose میشود. کد زیر
این روش دارای تمام خواص یک تراکنش است(اصطلاحا به این خواص ACID Properties گفته میشود)
1-Atomic : به این معناست که تمام دستورات بین بلاک (دستورات SQL و سایر عملیات) باید به صورت عملیات اتمی کار کنند. یعنی یا تمام عملیات موفقیت آمیز است یا همه با شکست روبرو میشوند.
2 - Consistent: به این معناست که اگر تراکنش موفقیت آمیز بود پایگاه داده باید در شروع تراکنش بعدی تغییرات لازم رو انجام داده باشد و در غیر این صورت پایگاه داده باید به حالت قبل از شروع تراکنش برگردد.
3- Isolated: اگر چند تا تراکنش هم زمان شروع شوند اجرای هیچ کدوم از اونها نباید بر اجرای بقیه تاثیر بزاره.
4- Durable: یعنی تغییرات حاصل شده بعد از اتمام تراکنش باید دائمی باشند.
روش کار به این صورت است تمام کارهایی که قصد داریم در طی یک تراکنش انجام شوند باید در یک بلاک قرار بگیرند و تا زمانی که متد Complete فراخوانی شود. در این بلاک شما هر عملیاتی رو که به عنوان جزئی از تراکنش میدونید قرار بدید. در صورتی که کنترل اجرا به فراخوانی دستور Complete برسه تمام موارد قبل از این دستور Commit میشوند در غیر این صورت RollBack.
به مثال زیر دقت کنید.
ابتدا به پروژه مربوطه باید اسمبلی System.Transaction رو اضافه کنید.
using ( TransactionScope scope = new TransactionScope() ) { //Statement1 //Statement2 //Statement3 scope.Complete(); }
using ( System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope() ) { if ( result == 0 ) { throw new ApplicationException(); } scope.Complete(); }
نکته حائز اهمیت این است که اگر در هنگام اجرای برنامه به این روش به خطای
MSDTC on server {} is unavailable
برخوردید باید سرویس MSDTC رو Start کنید.برای این کار باید سرویس Distributed Transaction Coordinator رو از لیست سرویسهای ویندوز پیدا کنید و بر روی اون راست کلیک کرده و دکمه Start رو بزنید.
نکته 1: میزان Timeout در این تراکنشها چه قدر است؟
برای بدست آوردن مقدار Timeout در این گونه تراکنشها میتوانید از کلاس TransactionManager استفاده کنید. به صورت زیر :
مقدار پیش فرض برای DefaultTimeout یک دقیقه است و برای MaximumTimeout ده دقیقه است. البته خاصیتهای بالا به صورت فقط خواندنی هستند و نمیتونید از این راه مقدار Timeout هر تراکنش را افزایش یا کاهش دهیم. برای این کار بهتره از روش زیر استفاده کنیم.
توضیح درباره انواع TransactionScopeOption
1 - Required : یعنی نیاز به تراکنش وجود دارد. در صورتی که تراکنش در یک تراکنش دیگر شروع شود نیاز به ساختن تراکنش جدید نیست و از همان تراکنش قبلی برای این کار استفاده میشود.
2 - RequiresNew: در هر صورت برای محدوده یک تراکنش تولید میشود.
3- Suppress : به عنوان محدوه تراکنش در نظر گرفته نمیشود.
مزایا استفاده از این روش
1-این روش از تراکنشهای توزیع شده پشتیبانی میکند . یعنی میتونید از چند تا منبع داده استفاده کنید یا میتونید از یک تراکنش چند تا Connection به یک منبع داده باز کنید.(استفاده از چند تاconnection در طی یک تراکنش)
2- پیاده سازی این روش واقعا راحت است .
3- با DataProviderهای متفاوت نظیر Oracle و OleDb و ODBC سازگار است.
4- از تراکنشهای تو در تو به خوبی پشتیبانی میکنه(Nested Transaction)
5- به خوبی توسط سرویسهای WCF پشتیبانی میشود و برای سیستمهای SOA مبتنی بر WCF مناسب است.معایب :
*استفاده از این روش در سیستم هایی که تعداد کاربران آنلاین آن زیاد است و هم چنین تعداد تراکنشهای موجود نیز در سطح سیستم خیلی زیاد باشه مناسب نیست.
*تراکنشهای استفاده شده از این روش کند هستند.(مخصوصا که تراکنش در سطح دیتابیس با تعداد و حجم داده زیاد باشه)
امکان تغییر IsolationLevel در طی انجام یک تراکنش امکان پذیر نیست.
(به شخصه مواد * رو در سطح یک پروژه با شرایط کاربران و حجم داده زیاد تست کردم و نتیجه مطلوب حاصل نشد)
موفق باشید.
نکته 1: میزان Timeout در این تراکنشها چه قدر است؟
برای بدست آوردن مقدار Timeout در این گونه تراکنشها میتوانید از کلاس TransactionManager استفاده کنید. به صورت زیر :
var defaultTimeout = TransactionManager.DefaultTimeout var maxTimeout = TransactionManager.MaximumTimeout
TransactionOptions option = new TransactionOptions(); option.Timeout = TimeSpan.MaxValue;
using ( System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope(TransactionScopeOption.Required ,option) ) { scope.Complete(); }
1 - Required : یعنی نیاز به تراکنش وجود دارد. در صورتی که تراکنش در یک تراکنش دیگر شروع شود نیاز به ساختن تراکنش جدید نیست و از همان تراکنش قبلی برای این کار استفاده میشود.
2 - RequiresNew: در هر صورت برای محدوده یک تراکنش تولید میشود.
3- Suppress : به عنوان محدوه تراکنش در نظر گرفته نمیشود.
using(TransactionScope scope1 = new TransactionScope()) { try { using(TransactionScope scope2 = new TransactionScope(TransactionScopeOption.Suppress)) { //به دلیل استفاده از Suppress این محدوده خارج از تراکنش محسوب میشود } //شروع محدوده تراکنش } catch {} //Rest of scope1 }
1-این روش از تراکنشهای توزیع شده پشتیبانی میکند . یعنی میتونید از چند تا منبع داده استفاده کنید یا میتونید از یک تراکنش چند تا Connection به یک منبع داده باز کنید.(استفاده از چند تاconnection در طی یک تراکنش)
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required)) { string strCmd = "SQL to Execute"; conn = new SqlClient.SqlConnection("Connection to DB1"); conn.Open() objCmd = new SqlClient.SqlCommand(strCmd, conn); objCmd.ExecuteNonQuery(); string strCmd2 = "SQL to Execute"; conn2 = new SqlClient.SqlConnection("Connection to DB2"); conn2.Open() objCmd2 = new SqlClient.SqlCommand(strCmd2, conn2); objCmd2.ExecuteNonQuery(); }
3- با DataProviderهای متفاوت نظیر Oracle و OleDb و ODBC سازگار است.
4- از تراکنشهای تو در تو به خوبی پشتیبانی میکنه(Nested Transaction)
using(TransactionScope scope1 = new TransactionScope()) { using(TransactionScope scope2 = new TransactionScope(TransactionScopeOption.Required)) { ... } using(TransactionScope scope3 = new TransactionScope(TransactionScopeOption.RequiresNew)) { ... } using(TransactionScope scope4 = new TransactionScope(TransactionScopeOption.Suppress)) { ... } }
*استفاده از این روش در سیستم هایی که تعداد کاربران آنلاین آن زیاد است و هم چنین تعداد تراکنشهای موجود نیز در سطح سیستم خیلی زیاد باشه مناسب نیست.
*تراکنشهای استفاده شده از این روش کند هستند.(مخصوصا که تراکنش در سطح دیتابیس با تعداد و حجم داده زیاد باشه)
امکان تغییر IsolationLevel در طی انجام یک تراکنش امکان پذیر نیست.
(به شخصه مواد * رو در سطح یک پروژه با شرایط کاربران و حجم داده زیاد تست کردم و نتیجه مطلوب حاصل نشد)
موفق باشید.