Run*_*une 38 .net c# sql-server ado.net transactions
假设某人(除了我)编写以下代码并将其编译为程序集:
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
using (var transaction = conn.BeginTransaction())
{
/* Update something in the database */
/* Then call any registered OnUpdate handlers */
InvokeOnUpdate(conn);
transaction.Commit();
}
}
Run Code Online (Sandbox Code Playgroud)
对InvokeOnUpdate(IDbConnection conn)的调用调用了一个我可以实现并注册的事件处理程序.因此,在这个处理程序中,我将引用IDbConnection对象,但我不会引用挂起的事务.我有什么办法可以控制交易吗?在我的OnUpdate处理程序中,我想执行类似于以下内容的操作:
private void MyOnUpdateHandler(IDbConnection conn)
{
var cmd = conn.CreateCommand();
cmd.CommandText = someSQLString;
cmd.CommandType = CommandType.Text;
cmd.ExecuteNonQuery();
}
Run Code Online (Sandbox Code Playgroud)
但是,对cmd.ExecuteNonQuery()的调用会抛出InvalidOperationException,抱怨它
"当分配给命令的连接处于挂起的本地事务中时,ExecuteNonQuery要求命令具有事务.该命令的Transaction属性尚未初始化".
我可以以任何方式使用挂起的事务登记我的SqlCommand cmd吗?我可以从IDbConnection对象中检索对待处理事务的引用(如果需要,我很乐意使用反射)?
Alv*_*uez 17
如果有人对反射代码感兴趣来完成这个,那么它在这里:
private static readonly PropertyInfo ConnectionInfo = typeof(SqlConnection).GetProperty("InnerConnection", BindingFlags.NonPublic | BindingFlags.Instance);
private static SqlTransaction GetTransaction(IDbConnection conn) {
var internalConn = ConnectionInfo.GetValue(conn, null);
var currentTransactionProperty = internalConn.GetType().GetProperty("CurrentTransaction", BindingFlags.NonPublic | BindingFlags.Instance);
var currentTransaction = currentTransactionProperty.GetValue(internalConn, null);
var realTransactionProperty = currentTransaction.GetType().GetProperty("Parent", BindingFlags.NonPublic | BindingFlags.Instance);
var realTransaction = realTransactionProperty.GetValue(currentTransaction, null);
return (SqlTransaction) realTransaction;
}
Run Code Online (Sandbox Code Playgroud)
笔记:
Jos*_*rke 10
哇我一开始并不相信.我很惊讶CreateCommand()在使用本地SQL Server事务时没有给它命令它的事务,并且事务没有在SqlConnection对象上公开.实际上,当反映SqlConnection当前事务时甚至不存储在该对象中.在下面的编辑中,我给了你一些提示,通过他们的一些内部类来追踪对象.
我知道你不能修改方法但是你可以在方法栏周围使用TransactionScope吗?所以如果你有:
public static void CallingFooBar()
{
using (var ts=new TransactionScope())
{
var foo=new Foo();
foo.Bar();
ts.Complete();
}
}
Run Code Online (Sandbox Code Playgroud)
这将有效,我使用类似的代码测试你的,一旦我添加包装所有工作正常,如果你可以这样做当然.正如所指出的那样,如果在TransactionScope您内部打开了多个连接,您将被升级为分布式事务,除非为您配置系统,否则您将收到错误消息.
加入DTC也比本地交易慢几倍.
如果你真的想尝试使用反射,SqlConnection有一个SqlInternalConnection,它依次有一个AvailableInternalTransaction属性,它返回一个SqlInternalTransaction,它有一个Parent属性,它返回你需要的SqlTransaction.