.NET C#事务处理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.NET C#事务处理相关的知识,希望对你有一定的参考价值。

具体怎么实现呢?请发一下代码。

有几种事务?

SqlConnection con = new SqlConnection(ConnectionDb.conStr);//获取数据库连接
con.Open();//打开连接
SqlTransaction sqltra = con.BeginTransaction();//开始事务
SqlCommand cmd = new SqlCommand();//实例化
cmd.Connection = con;//获取数据连接
cmd.Transaction = sqltra;//,在执行SQL时,
try

string sql = "insert into OA_MEETING(meeting_id,meeting_name,meeting_title,start_time,end_time,announce_time,explain,reg_emp_id,emc_emp_id,par_emp_id,rep_emp_id,participate,MEETING_TYPE,PROJ_ID,WORK_ID)values('" + id + "',N'" + meetname + "',N'" + meettitle + "','" + starttime + "','" + endtime + "',N'" + djtime + "',N'" + meetinfo + "','" + empid + "','" + meetperson + "','" + lname + "','" + writer + "','" + ccname + "','" + hidsort.Value + "','" + proid + "','" + workid + "')";
cmd.CommandText = sql;
cmd.ExecuteNonQuery();

//string meetid = id;
int size = 0;
HttpFileCollection files = HttpContext.Current.Request.Files;
//遍历上传文件窗体所有html控件
foreach (string NameId in Request.Form)

//找到控件ID名前四位为"txt_"的文件说明文本框
if (NameId.Substring(0, 4) == "ttx_")

//获取文本说明
if (Request.Form[NameId] != "")

string Explain = Request.Form[NameId];//获取txt文本说明

HttpPostedFile pstfile = files["fil_" + NameId.Substring(4)];
string fileName = "";
string fileExtension = "";
fileName = System.IO.Path.GetFileName(pstfile.FileName);//上传的文件全名
fileExtension = System.IO.Path.GetExtension(fileName);//扩展名
string NewName = fileName.Remove(fileName.LastIndexOf("."));//去掉后缀名的文件名
string sql1 = "select isnull(max(app_id),0) from OA_MEETING_APPENDIX";

int idd = Convert.ToInt32(objConnction.getString(sql1)) + 1;
ua.Folder_Exists("Meeting");
string filepath = "..\\AtthFiles\\Meeting\\Meeting_" + idd.ToString() + fileExtension;
string sqlstr1 = "insert into OA_MEETING_APPENDIX(APP_ID,MEETING_ID,EXPLAIN,APP_PATH) values('" + idd + "','" + id + "',N'" + Explain + "',N'" + filepath + "')";
cmd.CommandText = sqlstr1;

int num = cmd.ExecuteNonQuery();
if (num > 0)

pstfile.SaveAs(Server.MapPath("..\\AtthFiles\\Meeting\\Meeting_" + idd.ToString() + fileExtension));
size = pstfile.ContentLength / 1024;
objDocument.InsertDoc(5, idd, Explain, "", "", DateTime.Now.ToString("yyyy-MM-dd"), Convert.ToInt32(HempID.Value), filepath, size, hidwork.Value, hidpro.Value);




sqltra.Commit();
catch(Exception ex)

sqltra.Rollback();

参考资料:随手一写

参考技术A 只有一种事务,但是有几种隔离级别,可参见IsolationLevel枚举
给个例子你,例子是使用的DB2数据库,你可以改为其他的
private void button10_Click(object sender, EventArgs e)

DbProviderFactory factory = DbProviderFactories.GetFactory(@"IBM.Data.DB2");

DbConnection con = factory.CreateConnection();
con.ConnectionString =
@"Server=192.20.30.167:50000;Database=DEVP;UID=db2;PWD=db2;
CurrentSchema=TK;Connection Lifetime=60;Connection Reset=false;
Max Pool Size=5;Min Pool Size=3;Pooling=True;Connect Timeout=15";

DbCommand cmd = factory.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "select * from tbtickettype";
cmd.Connection = con;
cmd.CommandTimeout = 300;

DbDataAdapter da = factory.CreateDataAdapter();
da.SelectCommand = cmd;

con.Open();
DbTransaction tran = con.BeginTransaction(IsolationLevel.ReadCommitted);
da.SelectCommand.Transaction = tran;//在连接起事务后,必须指定命令的事务
try

DataSet ds = new DataSet();
da.Fill(ds);

ds.Tables[0].Rows[0]["TICKETNAME"] = "面部走引组()";
cmd = factory.CreateCommand();
cmd.CommandText = "update tk.TBTICKETTYPE set ticketname = '面部走引组(zzz)' where tickettype = 19";
cmd.Connection = con;
cmd.CommandTimeout = 300;
da.UpdateCommand = cmd;
da.UpdateCommand.Transaction = tran;
int a = da.Update(ds);//只有修改状态的才执行更新语句

DbConnection con1 = factory.CreateConnection();
con1.ConnectionString =
@"Server=172.20.30.107:50000;Database=BL_DEVP;UID=db2inst1;PWD=db2;
CurrentSchema=TK;Connection Lifetime=60;Connection Reset=false;
Max Pool Size=5;Min Pool Size=3;Pooling=True;Connect Timeout=15";

DbCommand cmd1 = factory.CreateCommand();
cmd1.CommandType = CommandType.Text;
cmd1.CommandText = "select * from tbtickettype";
cmd1.Connection = con1;
cmd1.CommandTimeout = 300;
da = factory.CreateDataAdapter();
da.SelectCommand = cmd1;
con1.Open();
DbTransaction tran1 = con1.BeginTransaction(IsolationLevel.ReadUncommitted);//区别就是可以脏读(及能读出事务过程中已经修改但未提交的数据)
da.SelectCommand.Transaction = tran1;
DataSet ds1 = new DataSet();
da.Fill(ds1);
tran1.Commit();
con1.Close();
tran.Commit(); //在提交前如果进行select * from tk.TBTICKETTYPE with ur就能读出尚未提交的更新,实际上就是ReadUnCommitted的效果
MessageBox.Show("OK");

catch

tran.Rollback();

finally

tran.Dispose();
con.Close();

参考技术B /// <summary>
/// 执行多条SQL语句,实现数据库事务。
/// </summary>
/// <param name="SQLStringList">SQL语句的哈希表(key为sql语句,value是该语句的SqlParameter[])</param>
public static void ExecuteSqlTranWithIndentity(Hashtable SQLStringList)

using (SqlConnection conn = new SqlConnection(connectionString))

conn.Open();
using (SqlTransaction trans = conn.BeginTransaction())

SqlCommand cmd = new SqlCommand();
try

int indentity = 0;
//循环
foreach (DictionaryEntry myDE in SQLStringList)

string cmdText = myDE.Key.ToString();
SqlParameter[] cmdParms = (SqlParameter[])myDE.Value;
foreach (SqlParameter q in cmdParms)

if (q.Direction == ParameterDirection.InputOutput)

q.Value = indentity;


PrepareCommand(cmd, conn, trans, cmdText, cmdParms);
int val = cmd.ExecuteNonQuery();
foreach (SqlParameter q in cmdParms)

if (q.Direction == ParameterDirection.Output)

indentity = Convert.ToInt32(q.Value);


cmd.Parameters.Clear();

trans.Commit();

catch

trans.Rollback();
throw;




http://www.xinqi8.com/c_mystery/Default.aspx

转:C#中TransactionScope的使用方法和原理

在.net 1.1的时代,还没有TransactionScope类,因此很多关于事务的处理,都交给了SqlTransaction和SqlConnection,每个Transaction是基于每个Connection的。这种设计对于跨越多个程序集或者多个方法的事务行为来说,不是非常好,需要把事务和数据库连接作为参数传入。

在.net 2.0后,TransactionScope类的出现,大大的简化了事务的设计。示例代码如下:

static void Main(string[] args)
        {
            using (TransactionScope ts = new TransactionScope())
            {
                userBLL u = new userBLL();
                TeacherBLL t = new TeacherBLL();
                u.ADD();
                t.ADD();
                ts.Complete();
            }
        }

 

只需要把需要事务包裹的逻辑块写在using (TransactionScope ts = new TransactionScope())中就可以了。从这种写法可以看出,TransactionScope实现了IDispose接口。除非显示调用ts.Complete()方法。否则,系统不会自动提交这个事务。如果在代码运行退出这个block后,还未调用Complete(),那么事务自动回滚了。在这个事务块中,u.ADD()方法和t.ADD()方法内部都没有用到任何事务类。

TransactionScope是基于当前线程的,在当前线程中,调用Transaction.Current方法可以看到当前事务的信息。具体关于TransactionScope的使用方法,已经它的成员方法和属性,可以查看 MSDN 。

TransactionScope类是可以嵌套使用,如果要嵌套使用,需要在嵌套事务块中指定TransactionScopeOption参数。默认的这个参数为Required。

该参数的具体含义可以参考http://msdn.microsoft.com/zh-cn/library/system.transactions.transactionscopeoption(v=vs.80).aspx

比如下面代码:

static void Main(string[] args)
        {
            using (TransactionScope ts = new TransactionScope())
            {
                Console.WriteLine(Transaction.Current.TransactionInformation.LocalIdentifier);
                userBLL u = new userBLL();
                TeacherBLL t = new TeacherBLL();
                u.ADD();
                using (TransactionScope ts2 = new TransactionScope(TransactionScopeOption.Required))
                {
                    Console.WriteLine(Transaction.Current.TransactionInformation.LocalIdentifier);
                    t.ADD();
                    ts2.Complete();
                }
               ts.Complete();
            }
        }

 

当嵌套类的TransactionScope的TransactionScopeOption为Required的时候,则可以看到如下结果,他们的事务的ID都是同一个。并且,只有当2个TransactionScope都complete的时候才能算真正成功。

技术分享

如果把TransactionScopeOption设为RequiresNew,则嵌套的事务块和外层的事务块各自独立,互不影响。

static void Main(string[] args)
        {
            using (TransactionScope ts = new TransactionScope())
            {
                Console.WriteLine(Transaction.Current.TransactionInformation.LocalIdentifier);
                userBLL u = new userBLL();
                TeacherBLL t = new TeacherBLL();
                u.ADD();
                using (TransactionScope ts2 = new TransactionScope(TransactionScopeOption.RequiresNew))
                {
                    Console.WriteLine(Transaction.Current.TransactionInformation.LocalIdentifier);
                    t.ADD();
                    ts2.Complete();
                }
              ts.Complete();
            }
        }

 

技术分享

可以看到,他们的事务id是不一样的。

TransactionScopeOption设为Suppress则为取消当前区块的事务,一般很少使用。

对于多个不同服务器之间的数据库操作,TransactionScope依赖DTC(Distributed Transaction Coordinator)服务完成事务一致性。

但是对于单一服务器数据,TransactionScope的机制则比较复杂。主要用的的是线程静态特性。线程静态特性ThreadStaticAttribute让CLR知道,它标记的静态字段的存取是依赖当前线程,而独立于其他线程的。既然存储在线程静态字段中的数据只对存储该数据的同一线程中所运行的代码可见,那么,可使用此类字段将其他数据从一个方法传递到该第一个方法所调用的其他方法,而且完全不用担心其他线程会破坏它的工作。TransactionScope 会将当前的 Transaction 存储到线程静态字段中。当稍后实例化 SqlCommand 时(在此 TransactionScope 从线程局部存储中删除之前),该 SqlCommand 会检查线程静态字段以查找现有 Transaction,如果存在则列入该 Transaction 中。通过这种方式,TransactionScope 和 SqlCommand 能够协同工作,从而开发人员不必将 Transaction 显示传递给 SqlCommand 对象。实际上,TransactionScope 和 SqlCommand 所使用的机制非常复杂。具体可以参考文章http://www.microsoft.com/china/MSDN/library/netFramework/netframework/NETMattersSep.mspx?mfr=true

Wrox出版的《Professional C# 4 and .NET 4》也有关于TransactionScope的一些使用方法的介绍。

以上是关于.NET C#事务处理的主要内容,如果未能解决你的问题,请参考以下文章

.NET C#事务处理

关于django出现重复点击,多事务处理

Mybatis事务处理

面试官:小伙子你给我说说MySq|并发事务处理细节

NetCore微服务实现事务一致性masstransit之saga使用

CQRS + 微服务处理事件回滚