PDA

View Full Version : استفاده از transaction



elahe1364
دوشنبه 25 خرداد 1388, 09:50 صبح
با سلام
من میحوام چند تا عمل مختلف رو پشت سر هم روی بانک انجام بدم و اگه تحت شرایط خاصی یکی از این اعمال انجام نشد بقیه هم انجام نشه. میدونم برای این کار باید از transaction استفاده کنم ولی یه مشکل دارم.

اگر من دستورات sql رو داخل خود c# بنویسم transaction عمل میکنه. اما من دستورات کار با بانکم رو داخل storeprocedure خود sql نوشتم و این پروسیجرها رو داخل متدهای کلاسم صدا زدم.
در این حالت من داخل برنامه سی شارپم فقط متدها رو صدا میزنم. تو این حالت transaction اون طوری که من میخوام عمل نمیکنه.
لطفا راهنماییم کنید.

نمونه کد من به این شکله

private void button1_Click(object sender, EventArgs e)
{
SqlTransaction tn;
SqlConnection cn = Program.creatCon();
try { if (cn.State != ConnectionState.Open) { cn.Open(); } }
catch (SqlException) { }
tn = cn.BeginTransaction();
Class1 ClsInsert = new Class1();
try
{
DataTable dt = ClsInsert.InsertDocTpdm_Tpd("88/2/25", 1, "abc", 163, 58);
Int64 i = Int64.Parse(dt.Rows[0][0].ToString());
if (i != 0)
tn.Rollback();
else
tn.Commit();
}
catch (SqlException) { tn.Rollback(); }
finally
{ if (cn.State != ConnectionState.Closed) { cn.Close(); } }

}

NewFoxStudent
دوشنبه 25 خرداد 1388, 10:05 صبح
ممکنه کد این تابع رو بزارید


ClsInsert.InsertDocTpdm_Tpd

elahe1364
دوشنبه 25 خرداد 1388, 10:44 صبح
بله ببخشید

public DataTable InsertDocTpdm_Tpd(string Fd, byte Kind, string TitleAcc, int FddNum, int NumForm)
{
SqlConnection con = Program.creatCon();
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
SqlDataAdapter da = new SqlDataAdapter(cmd);
cmd.CommandType = CommandType.StoredProcedure;
DataTable dt = new DataTable();
cmd.Parameters.AddWithValue("@Fd", Fd);
cmd.Parameters.AddWithValue("@Ft", "12:30");
cmd.Parameters.AddWithValue("@Kind", Kind);
cmd.Parameters.AddWithValue("@Fop", 1);
cmd.Parameters.AddWithValue("@TitleAcc", TitleAcc);
cmd.Parameters.AddWithValue("@FddNum", FddNum);
cmd.Parameters.AddWithValue("@NumForm", NumForm);
con.Open();
cmd.CommandText = "Insert_Tdm";
da.Fill(dt);
cn.Close();
return dt;
}

NewFoxStudent
دوشنبه 25 خرداد 1388, 13:03 عصر
public DataTable InsertDocTpdm_Tpd(string Fd, byte Kind, string TitleAcc, int FddNum, int NumForm)
{
SqlConnection con = Program.creatCon();
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
SqlDataAdapter da = new SqlDataAdapter(cmd);
cmd.CommandType = CommandType.StoredProcedure;
DataTable dt = new DataTable();
cmd.Parameters.AddWithValue("@Fd", Fd);
cmd.Parameters.AddWithValue("@Ft", "12:30");
cmd.Parameters.AddWithValue("@Kind", Kind);
cmd.Parameters.AddWithValue("@Fop", 1);
cmd.Parameters.AddWithValue("@TitleAcc", TitleAcc);
cmd.Parameters.AddWithValue("@FddNum", FddNum);
cmd.Parameters.AddWithValue("@NumForm", NumForm);
cmd.CommandText = "Insert_Tdm";
con.Open();
SqlTransaction tran = con.BeginTransaction();
da.Fill(dt);
if (myCondition)
tran.Commit();
else
tran.Rollback();
cn.Close();
return dt;
}
این رو امتحان کنید
به جای MyCondition شرطی رو که قراره اتفاق بیفته قرار بدید به عنوان مثال (تعداد رکوردهای دریافت شده بیشتر از 1 باشه) یا هرچی

elahe1364
دوشنبه 25 خرداد 1388, 13:15 عصر
ولی من خیلی جاهای برنامم حتی بدون اینکه شرطی داشته باشم میخوام تمام عملیاتم با هم انجام بشه.

ولی الان که با کلاس ها کار کردم transaction عمل نمیکنه.

NewFoxStudent
دوشنبه 25 خرداد 1388, 13:25 عصر
ببینید ترنزکشن برای اینه که شما یه مجموعه دستوری مرتبط به هم رو اجرا کنید و اطمینان داشته باشید که همه این دستورات به صورت صحیح اجرا شدن حالا اینکه چطور متوجه میشید که این دستورات تا آخر اجرا شدن بستگی به کاری داره که میخواهید انجام بدید و این همون شرطی که باید بعد از اجرای دستورات چک کنید
به عنوان مثال دوتا جدول داریم که یکی مشخصات شناسنامه ای پرسنل و دیگری مشخصات حکمی
کاربر موقع تعریف پرسنل هر دونوع مشخصات رو با هم تو فرم وارد میکنه
حالا ما باید اول مشخصات شناسنامه ای رو ذخیره کنیم و در صورتی که مشخصات شناسنامه به صورت صحیح ذخیره شد بعد مشخصات حکمی رو هم ذخیره کنیم
برای این کار دستورات Insert هر دو جدول رو داخل یک Transaction اجرا میکنیم و در آخر مثلا با یه Select یا هر چیز دیگه ای میفهمیم که آیا هر دو دستور اجرا شدن یا نه و اگه شده بود Tran رو Commit میکنیم و در غیر این صورت اون رو RollBack میکنیم
منظور من از شرط این قسمتی که قرمزش کردم

elahe1364
دوشنبه 25 خرداد 1388, 18:56 عصر
دوست عزیز یعنی اگه من بخوام 5 تا 6 تا درج رو پشت سر هم انجام بدم و 5 تا متد رو پشت سر هم صدا بزنم باید توی تمام متدهای کلاسام transaction بذارم؟ همون دستوری که شما نوشتید؟

NewFoxStudent
دوشنبه 25 خرداد 1388, 19:08 عصر
دوست عزیز یعنی اگه من بخوام 5 تا 6 تا درج رو پشت سر هم انجام بدم و 5 تا متد رو پشت سر هم صدا بزنم باید توی تمام متدهای کلاسام transaction بذارم؟ همون دستوری که شما نوشتید؟


منظورتون رو واضح تر بگید
شما میخواهید چند تا عملیات درج رو که هر کدوم مربوط به یک کلاس جداگانه ست داخل یک Transaction انجام بدید؟

SMRAH1
دوشنبه 25 خرداد 1388, 21:40 عصر
سلام



برای این کار دستورات Insert هر دو جدول رو داخل یک Transaction اجرا میکنیم و در آخر مثلا با یه Select یا هر چیز دیگه ای میفهمیم که آیا هر دو دستور اجرا شدن یا نه و اگه شده بود Tran رو Commit میکنیم و در غیر این صورت اون رو RollBack میکنیم


دوستان،فقط برای یادآوری عرض می کنم که به هر دلیل که برنامه نتواند عملیات مورد نظر ما را در پایگاه داده انجام دهد،یک Exception رخ خواهد داد.در نتیجه باید یک block از try..Catch..Finally نوشته و در آخرین خط try عملیات Commit و در صورت وقوع استثنا عملیات RollBack انجام بشه یعنی:


try
{
// Begin Transact
// Do Something
// Commit Transact
}
catch(..)
{
// RollBack Transact
}البته می توان بر روی Catch های مختلف عملیات های متفاوتی انجام داد (بسته به سناریویی که پیاده سازی می شود)

موفق باشید

elahe1364
سه شنبه 26 خرداد 1388, 11:10 صبح
بله من ممکنه نیاز داشته باشم چند متد مختلف از یک کلاس و یا چند متد از چند کلاس رو پشت سرهم اجرا کنم و همه اینها وابسته به هم هست.

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

من زمانی که دستورات sql رو داخل خود c# مینویسم transaction همینطور که میخوام عمل میکنه ،ولی زمانی که دستورات کار با بانک رو داخل پروسیجر sql بنویسم و به وسیله کلاس ها و متدها با اونها کار کنم ، حتی زمانیکه روی یک خط exception رخ میده خط های قبلی تغییراتشون رو روی بانک اعمال کردند.

اگه شما نمونه ای دارید که هم با استورپروسیجر و کلاس و هم با transaction کار کردید و درست جواب میده برام بذارید ممنون میشم.

SMRAH1
سه شنبه 26 خرداد 1388, 17:46 عصر
سلام

دوست من شاید ایراد از Connection است.در واقع هر دستور رو با یک Connection مستقل ایجاد میکنید که باعث ایجاد مشکل میشه.برای رفع این ایراد باید ابتدا یک Connection بسازید و بعد در هر دستور (یا آداپتور و یا ...) از اون Connection استفاده کنید.همون Connection هم در ابتدا به Transact میره و در نهایت تایید یا رد میشه.

اما اگر این مشکل وجود نداره ،توصیه میکنم کدتون رو (فقط کد برنامه رو و البته به صورت کامل تر، مثلا متد Program.creatCon در برنامه چیست و ...) رو بگذارید تا بهتر بررسی کنیم.

موفق باشید

elahe1364
شنبه 30 خرداد 1388, 11:11 صبح
بله دوست عزیز من برای هر متدم مجددا کانکشن رو وصل میکنم
اینم کد کاملش .ممنون میشم راهنماییم کنید که چه تغییری داخل این کد بدم.

این کد داخل رویداد


private void button1_Click(object sender, EventArgs e)
{
SqlTransaction tn;
SqlConnection cn = Program.creatCon();
try { if (cn.State != ConnectionState.Open) { cn.Open(); } }
catch (SqlException) { }
tn = cn.BeginTransaction();
Class1 ClsInsert = new Class1();
try
{
DataTable dt = ClsInsert.InsertDocTpdm_Tpd("88/2/25", 1, "abc", 163, 58);
Int64 i = Int64.Parse(dt.Rows[0][0].ToString());
if (i != 0)
tn.Rollback();
else
tn.Commit();
}
catch (SqlException) { tn.Rollback(); }
finally
{ if (cn.State != ConnectionState.Closed) { cn.Close(); } }

}


اینم کد متد داخل کلاسم:


public DataTable InsertDocTpdm_Tpd(string Fd, byte Kind, string TitleAcc, int FddNum, int NumForm)
{
SqlConnection con = Program.creatCon();
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
SqlDataAdapter da = new SqlDataAdapter(cmd);
cmd.CommandType = CommandType.StoredProcedure;
DataTable dt = new DataTable();
cmd.Parameters.AddWithValue("@Fd", Fd);
cmd.Parameters.AddWithValue("@Ft", "12:30");
cmd.Parameters.AddWithValue("@Kind", Kind);
cmd.Parameters.AddWithValue("@Fop", 1);
cmd.Parameters.AddWithValue("@TitleAcc", TitleAcc);
cmd.Parameters.AddWithValue("@FddNum", FddNum);
cmd.Parameters.AddWithValue("@NumForm", NumForm);
con.Open();
cmd.CommandText = "Insert_Tdm";
da.Fill(dt);
cn.Close();
return dt;
}


و این کد هم داخل program.cs نوشتم برای اتصال به بانک:


public static SqlConnection creatCon()
{
SqlConnection con = new SqlConnection("server=(local);database=AAS;uid=sa;pwd=123;trusted _connection=yes");
return con;
}

SMRAH1
شنبه 30 خرداد 1388, 22:36 عصر
سلام

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


static class Program
{
private static SqlConnection con = null;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
//Any code is here.NO CHANGE
}

public static SqlConnection CreateConnection()
{
if(con==null)
con = new SqlConnection("server=(local);database=AAS;uid=sa;pwd=123;trusted _connection=yes");
return con;
}

public static SqlConnection DeleteConnection()
{
con==null;
}
}


موفق باشید