PDA

View Full Version : مشکل در استفاده transaction ها



minaalamshahi
دوشنبه 21 فروردین 1391, 12:01 عصر
با سلام
من لازم داشتم که توی پروژه ام چندین رکورد برای یه کاربر update بشه با چک شرایطی یا اصلا اگه شرایط درست نبود هیچ کدوم انجام نشه
قبل از ورود به transaction شرط رو چک می کنم اگه درست بود وارد transaction میشم و حالا میخوام db برای این کاربر قفل بشه و تمام سطرها براش ویرایش بشه (قفل شدن رو برای این لازم دارم که شخصی که ظرفیت هایی از یک کلاس رو انتخاب کرده اول این کلاس ها برای اون ثبت نهایی بشه و در حین اینکه این کاربر داره کلاس های مورد نظرش رو ثبت نهایی می کنه کاربر دیگه ای اونا رو انتخاب نکنه)
برای همین یه select زدم و تمام سطرهای مربوط به اون رو می گیرم و حالا سطر به سطر می خوام داخل transaction ویرایششون کنم
برای اینکار از دستورات زیر استفاده می کنم

using (TransactionScope transactionScope = new TransactionScope())
{
ds.Clear();
ds=admin.SearchAllHighSelectByTeacherIdIsNotTemp(l ong.Parse(Session["HighTeacherId"].ToString()),long.Parse(Session["HighSetting_Id"].ToString()),out except);

for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
ErrorCode = admin.ChangeCapacityHighSelect(long.Parse(ds.Table s[0].Rows[i]["HighSchool_Id"].ToString()), long.Parse(ds.Tables[0].Rows[i]["HighCatena_Id"].ToString()), long.Parse(ds.Tables[0].Rows[i]["HighLesson_Id"].ToString()), ds.Tables[0].Rows[i]["HighSelect_Level"].ToString(), long.Parse(ds.Tables[0].Rows[i]["HighTeacher_Id"].ToString()), long.Parse(ds.Tables[0].Rows[i]["HighSetting_Id"].ToString()), out except);
if (ErrorCode == 0)
{
//nothing
}
else
{
State = true;
}
}
if (State == false)
transactionScope.Complete();
}

////////////////////////////
public int ChangeCapacityHighSelect(Int64 HighSchool_Id, Int64 HighCatena_Id, Int64 HighLesson_Id, string HighSelect_Level, Int64 HighTeacher_Id, Int64 HighSetting_Id, out Exception except)
{
int pc = 0;
try
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Sp_ChangeCapacity";
cmd.Parameters.Clear();

cmd.Parameters.Add("@HighSchool_Id", SqlDbType.BigInt).Value = HighSchool_Id;
cmd.Parameters.Add("@HighCatena_Id", SqlDbType.BigInt).Value = HighCatena_Id;
cmd.Parameters.Add("@HighLesson_Id", SqlDbType.BigInt).Value = HighLesson_Id;
cmd.Parameters.Add("@HighSelect_Level", SqlDbType.NVarChar, 50).Value = HighSelect_Level;
cmd.Parameters.Add("@HighTeacher_Id", SqlDbType.BigInt).Value = HighTeacher_Id;
cmd.Parameters.Add("@HighSetting_Id", SqlDbType.BigInt).Value = HighSetting_Id;
 
foreach (SqlParameter Parameter in cmd.Parameters)
if (Parameter.Value == null)
Parameter.Value = DBNull.Value;
connection.Open();
pc = int.Parse(cmd.ExecuteScalar().ToString());
Except.Data["code"] = "0";
Except.Data["Error"] = "";
Except.Data["Level"] = "";
}
catch (Exception ee)
{
Except.Data["code"] = "1";
Except.Data["Error"] = ee.Message;
Except.Data["Level"] = "Entity." + sourceName;
}
finally
{
except = Except;
connection.Close();
}
return pc;
}

به خط
connection.Open();

که میرسه exception میده
The operation is not valid for the state of the transaction.
پس چطور ازدستورات مربوط به دیتا بیس در اینجا استفاده کنم

d_derakhshani
دوشنبه 21 فروردین 1391, 13:18 عصر
اول اینکه می تونی سطر به سطر انجام ندی و خود sp تمام سطرهات و ویرایس کنه(یعنی فراخوانی یک sp و فقط یکبار)
اما برای سطر به سطر ویرایش کردن:
connection باید قبل از حلقه for انجام بشه(یا باید connection رو public کنی تو سطح کلاس یا حلقه for رو به نحوی ببری داخل تابع(یعنی تابع، تابع ویرایش جمعی بشه نه تکی. این برای تمرین به عهده خودت).
اما برای استفاده از transaction:

connection.Open();
Transaction t=connection.BeginTransaction()
for
try{
Execute
}
catch{
t.RollBack();
connection.Close();
return false//return false to specifiyed no operation executed
}
end of for
.
.
.
t.Commit();
connection.Close();
return true;


الحاقیه:
کد شما دو مشکل داره»
یک( مشکل اصلی): اینکه در حلقه for تعداد زیادی کانکشن ساخته میشه(و باز و بسته میشه). در یک transaction scope فقط می تونید یک connection استفاده کنید. خطا شما هم به همین علت هست
دو اینکه در داخل بلاک transaction scope تابع فراخوانی میشه و در داخل تابع try-catch هست از اونجا که using استفاده کردید نباید در داخل using دستور try-catch باشه تا transaction scope بتونه خدکار rollback کنه. حالا که استفاده کردید آنجا که compelet می کنید باید در صورت وجود خطا RollBack هم دستی انجام بدید.

minaalamshahi
دوشنبه 21 فروردین 1391, 13:39 عصر
connection تا اجرای کامل حلقه باز بمونه؟

d_derakhshani
دوشنبه 21 فروردین 1391, 13:46 عصر
100% چون داخل حلقه دستور execute هست پس یا در هر اجرا باید باز کنی و سریعا پشت سرش ببندی و یا کلا باز بذاری. خوب وقتی یک connection تا بسته شد باید باز بشه(و باز کردن می تونه سربار اضافی داشته باشه)چرا باید ببندی دوباره باز کنی؟مسلما اگه باز بمونه بالاترین کارایی رو در این مورد داره

minaalamshahi
دوشنبه 21 فروردین 1391, 13:50 عصر
یه سوال آیا در حین استفاده از transaction هیچ کاربر دیگه ای نمی تونه با db کار کنه تا کاری که کاربر اول داره انجام میده تموم بشه؟
می خوام مطمئن بشم که هیچ کاربر دیگه ای کلاس رو که الان کاربر 1 گرفته و داره تایید می کنه رو نگیره .
و کسی اول بتونه کلاس 1 و2و... رو تایید نهایی کنه که زودتر تایید نهایی رو بزنه.
و در این حین هیچ کاربری نتئنه کلاس هایی رو که این کاربر تایید نهایی می کنه انتخاب کنه

d_derakhshani
دوشنبه 21 فروردین 1391, 14:03 عصر
چرا می تونه کار کنه
باید از lock استفاده کنید تا شخص دیگه ای نتونه قسمتی رو که می خواید دستکاری کنه

p.yazdkhasti
دوشنبه 21 فروردین 1391, 14:08 عصر
سلام
می توانید از TransactionScope بدون Try-Catch به صورت Explicit هم استفاده کنید. مانند کد زیر :


using (TransactionScope scope = new TransactionScope())
{
using (SqlConnection connection1 = new SqlConnection(connectString))
{
scope.Complete (); }
}

d_derakhshani
دوشنبه 21 فروردین 1391, 14:26 عصر
دوست عزیز TRy-catch ارتباطی با transactionScop نداره، try-catch مربوط به اجرای دستور Execute میشه که ممکنه با خطا مواجه بشه. در نتیجه در هر صورت باید try-catch استفاده بشه
TransactionScope متد Commit نداره و باید از Complete استفاده بشه

p.yazdkhasti
دوشنبه 21 فروردین 1391, 14:54 عصر
دوست عزیز TRy-catch ارتباطی با transactionScop نداره، try-catch مربوط به اجرای دستور Execute میشه که ممکنه با خطا مواجه بشه. در نتیجه در هر صورت باید try-catch استفاده بشه
TransactionScope متد Commit نداره و باید از Complete استفاده بشه
سلام
با استفاده از عبارت Using نیازی به Rollback کردن transaction با وقوع خطا در try-catch نمی باشد. برای اطلاعات بیشتر می توانید به لینک زیر مراجعه کنید:
http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx
در ارتباط با متد Commit نیز اشتباه از بنده بود چون من در محیط فروم مشغول نوشتن کد بودم متوجه این اشتباه نشدم. نام متد Complete است.

d_derakhshani
دوشنبه 21 فروردین 1391, 15:06 عصر
بله احتیاجی به متد RollBack نیست که در try-catch باشه اما خود try-catch باید بدلیل وجود دستور execute بمونه. دفت کنید که در Sample خود ماکروسافت Try-catch وجود داره

p.yazdkhasti
دوشنبه 21 فروردین 1391, 15:15 عصر
بله احتیاجی به متد RollBack نیست که در try-catch باشه اما خود try-catch باید بدلیل وجود دستور execute بمونه. دفت کنید که در Sample خود ماکروسافت Try-catch وجود داره
اگر با فراخوانی متد Execute خطایی رخ دهد Transaction رول بک خواهد شد و Connection بسته خواهد شد و نیازی به try-catch برای Rollback کردن Transaction و بستن Connection در کد ذکر شده توسط اینجانب نمی باشد. در مثال Microsoft در لینک ذکر شده نیز عبارت try-catch قبل از عبارت using استفاده شده است و در catch آن تنها پیغام خطا در Console نوشته شده است و عملیات دیگری مانند Rollback کردن Transaction و یا بستن Connection در Catch آن قرار داده نشده است.

minaalamshahi
سه شنبه 22 فروردین 1391, 09:29 صبح
لینکی که داده بودین باز نمیشه میشه یه مثال کوچیک از transaction ,lock بهم بدین؟طبق توضیحاتی که دادم میخوام هر دو رو با هم انجام بدم؟

minaalamshahi
سه شنبه 22 فروردین 1391, 10:02 صبح
از توضیحاتی که میدیدن ممنون ولی مگه نحوه استفاده TRANSACTION به طریقی که من نوشتم اشتباهه؟اگه امکانش هست یه SAPMLE برام بذارین ممنون میشم

d_derakhshani
سه شنبه 22 فروردین 1391, 11:13 صبح
اگر با فراخوانی متد Execute خطایی رخ دهد Transaction رول بک خواهد شد و Connection بسته خواهد شد و نیازی به try-catch برای Rollback کردن Transaction و بستن Connection در کد ذکر شده توسط اینجانب نمی باشد. در مثال Microsoft در لینک ذکر شده نیز عبارت try-catch قبل از عبارت using استفاده شده است و در catch آن تنها پیغام خطا در Console نوشته شده است و عملیات دیگری مانند Rollback کردن Transaction و یا بستن Connection در Catch آن قرار داده نشده است.
دوست گرام شما دستور using استفاده کردید، وقتی دستور using استفاده می کنید احتیاجی به close کردن connection و RollBack کردن transactionScop نیست. این چیز واضحی هست و از خواص using هست. using تضمین میکنه اگه در هرجای برنامه خطا رخ بده عملیات close و rollback رو انجام میده اما تضمین نمیکنه خطا رو هندل کنه. در نتیجه نباید در هنگام استفاده از using از try-catch داخلی استفاده بشه تا using متوجه خطا بشه و عملیات نهایی خودش رو انجام بده. صحبت من اینه که شما چون using استفاده کردید لازم به try-catch داخلی نیست و لاغیر و در هر صورت try-catch لازمه.
برای مثال من دستور قبلی رو با using باز نویسی می کنم:

try
{
using (SqlConnection connection1 = new SqlConnection(connectString))
{
using(Transaction t=connection.BeginTransaction())
{
for{
Execute
}//end of for
}
t.Commit();
}


catch
{
return false//return false to specifiyed no operation executed
}

return true;

فکر کنم الان متوجه منظور من شده باشید که چی میخواستم بگم(قسمتی از پستتون که نوشته بودید احتیاج به try-catch نیست موجب گمراهی خواننده میشه)

minaalamshahi
سه شنبه 22 فروردین 1391, 12:13 عصر
طبق روال که ما در گروه داریم و با معماری سه لایه استفاده می کنیم CONNECTION رو یکبار تعریف کردیم و دستورات مربوط به SQL رو هم به همون روالی که بهتون نشون دادم تعریف می کنیم اما اگر بخوام به این روشی که شما می فرمایید عمل کنم روال کار به هم میریزه

minaalamshahi
سه شنبه 22 فروردین 1391, 13:16 عصر
من دستورات رو یه بار دیگه نوشتم با این تفاوت که تابعی که برای فراخوانی sp بود یه مشکل پارامتر داشت که حل کردم
حالا دستورات همون طور که در پست اول بود دوباره اجرا کردم و اجرا شد
روال کار رو که نگاه می کنم اگر وسط اجرای for پنجره رو ببندم و اجرا رو متوقف کنم هیچی درج نمیشه ولی اگر اجرا به دستور complete برسه یکباره همه رکورد ها ویرایش میشه
گفته بودین حالا که دارم به این روش استفاده می کنم rollback رو دستی باید انجام بدم.چطور Rollback کنم؟