我正在编写一个用于插入 2 个表的 Web 服务方法。这是我的代码:
MLL_Result _paClient = new MLL_Result();
lock (thisLock)
{
using (SqlConnection connection = new SqlConnection(ConfigurationSettings.AppSettings["Main.ConnectionString"]))
{
connection.Open();
SqlTransaction transaction;
transaction = connection.BeginTransaction("SampleTransaction");
try
{
// Do some insert / update here
#region FIRST
Tbl_Patient patientObject = new Tbl_Patient();
string[] spID = sid.Split('-');
patientObject.PID = pid;
patientObject.SID = sid;
patientObject.Seq = spID[1];
patientObject.Address = address;
patientObject.Phone = phone;
_paClient.Insert(patientObject, connection, transaction);
#endregion
/////
#region LAST
if (_interClient.CheckExist(patientObject.PID) == 0)
{
Tbl_Inter inteObject = new Tbl_Inter();
inteObject.PID = patientObject.PID;
inteObject.Address = patientObject.Address;
inteObject.DateIN = patientObject.DateIN;
_paClient.Insert(inteObject, connection, transaction);
}
#endregion
}
transaction.Commit();
}
catch (Exception ex)
{
#region Catch Exception
//Console.WriteLine("Commit Exception Type: {0}", ex.GetType());
//MessageBox.Show(" Message:" + ex.Message);
// Attempt to roll back the transaction.
try
{
transaction.Rollback();
}
catch (Exception ex2)
{
// This catch block will handle any errors that may have occurred
// on the server that would cause the rollback to fail, such as
// a closed connection.
//Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType());
//MessageBox.Show(" Message:" + ex2.Message);
}
#endregion
}
}
当流量较低时,它确实工作正常。但当流量较高时,会产生如下错误:
The server failed to resume the transaction Desc ... The transaction active in the session has been commited or aborted by another session.
请帮我解决这个问题:((像这样同时使用锁和事务是否会相互冲突?
最佳答案
有几件事让我突然想到。
您可能会遇到与事务相关的序列化问题,即您在事务连接上插入值,然后检查刚刚在不同的非事务连接上插入的值是否存在。如果您的事务隔离级别设置为“可序列化”,则读取操作将被阻止直到超时。
无论如何,任何类型的“稀缺”资源管理(在本例中为数据库事务)的最佳实践都是晚获取、早释放。您希望在开始事务和提交(或回滚)之间花费尽可能少的时间和 CPU 周期。在事务打开的整个过程中,数据库服务器正在同步对资源的访问、锁定表的范围等。
因此,您可能会考虑在事务外部而不是事务内部填充 DTO。
我认为 _paClient
和 _interClient
都是该代码所在类的成员变量?我看到您将连接传递给 _paClient
,但没有传递给 _interClient
,因此 _interClient
不能参与与 _paClient< 相同的事务
。 _interClient
从哪里获取数据库连接?如果 _interClient 正在启动自己的数据库连接,那么您使用的数据库是否足以支持每个客户端多个连接?在原始连接上启动事务后启动新连接可能会破坏原始连接。我不知道您正在使用什么数据库,也不知道此类事情可能会在该数据库的驱动程序堆栈中导致什么样的行为。
这里真的需要lock (thisLock)
吗?一般来说,锁定对性能没有帮助。但如果有必要,那就有必要。
嵌套的 try/catch 可能有点过分了。您将在调用堆栈的边缘进行异常处理和日志记录,从而捕获此处的失败,对吗?
我会考虑为实际事务管理采用稍微不同的结构,可能更像下面重新编写的代码。
请注意,您可以省略显式回滚代码并让 Transaction 对象的 Dispose() 方法负责回滚,但该行为是特定于提供程序的(它可以与 SQL Server 正常工作),并且您可能无法100%依赖它。
Tbl_Patient patientObject = new Tbl_Patient();
string[] spID = sid.Split('-');
patientObject.PID = pid;
patientObject.SID = sid;
patientObject.Seq = spID[1];
patientObject.Address = address;
patientObject.Phone = phone;
Tbl_Inter inteObject = null;
// does this CheckExist function have to hit the database, or can it avoid that?
if (_interClient.CheckExist(patientObject.PID) == 0)
{
inteObject = new Tbl_Inter();
inteObject.PID = patientObject.PID;
inteObject.Address = patientObject.Address;
inteObject.DateIN = patientObject.DateIN;
}
using (var con = new SqlConnection(ConfigurationSettings.AppSettings["Main.ConnectionString"]))
{
con.Open();
using( var tx = con.BeginTransaction("SampleTransaction") )
{
try
{
_paClient.Insert(patientObject, con, tx);
if( null != inteObject )
{
_paClient.Insert(inteObject, con, tx);
}
tx.Commit();
}
catch( Exception ex )
{
try{ transaction.Rollback(); }
catch( Exception ex2 )
{
// lame that this is necessary if you're not using implicit rollback
// write to log, whatever...
}
throw; //re-throw the original exception w/call stack for logging by your global exception handler
}
}
}
关于c# - 服务器无法恢复事务 事务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25072993/