# برنامه نویسی با محصولات مایکروسافت > برنامه نویسی مبتنی بر Microsoft .Net Framework > دسترسی به داده ها (ADO.Net و LINQ و ...) >  استفاده از transaction

## elahe1364

با سلام
من میحوام چند تا عمل مختلف رو پشت سر هم روی بانک انجام بدم و اگه تحت شرایط خاصی  یکی از این اعمال انجام نشد بقیه هم انجام نشه. میدونم برای این کار باید از 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

ممکنه کد این تابع رو بزارید

ClsInsert.InsertDocTpdm_Tpd

----------


## elahe1364

بله ببخشید
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

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

ولی من خیلی جاهای برنامم حتی بدون اینکه شرطی داشته باشم میخوام تمام عملیاتم با هم انجام بشه.

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

----------


## NewFoxStudent

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

----------


## elahe1364

دوست عزیز یعنی اگه من بخوام 5 تا 6 تا درج رو پشت سر هم انجام بدم و 5 تا متد رو پشت سر هم صدا بزنم باید توی تمام متدهای کلاسام transaction بذارم؟ همون دستوری که شما نوشتید؟

----------


## NewFoxStudent

دوست عزیز یعنی اگه من بخوام 5 تا 6 تا درج رو پشت سر هم انجام بدم و 5 تا متد رو پشت سر هم صدا بزنم باید توی تمام متدهای کلاسام transaction بذارم؟ همون دستوری که شما نوشتید؟


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

----------


## SMRAH1

سلام




> برای این کار دستورات 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

بله من ممکنه نیاز داشته باشم چند متد مختلف از یک کلاس و یا چند متد از چند کلاس رو پشت سرهم  اجرا کنم و همه اینها وابسته به هم هست.

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

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

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

----------


## SMRAH1

سلام

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

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

موفق باشید

----------


## elahe1364

بله دوست عزیز من برای هر متدم مجددا کانکشن رو وصل میکنم 
اینم کد کاملش .ممنون میشم راهنماییم کنید که چه تغییری داخل این کد بدم.

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

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

سلام

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

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;
    }
}


موفق باشید

----------

