假设某人(除了我)编写以下代码并将其编译为程序集:
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(); } }
对InvokeOnUpdate(IDbConnection conn)的调用调用了一个我可以实现并注册的事件处理程序.因此,在这个处理程序中,我将引用IDbConnection对象,但我不会引用挂起的事务.我有什么办法可以控制交易吗?在我的OnUpdate处理程序中,我想执行类似于以下内容的操作:
private void MyOnUpdateHandler(IDbConnection conn) { var cmd = conn.CreateCommand(); cmd.CommandText = someSQLString; cmd.CommandType = CommandType.Text; cmd.ExecuteNonQuery(); }
但是,对cmd.ExecuteNonQuery()的调用会抛出InvalidOperationException,抱怨它
"当分配给命令的连接处于挂起的本地事务中时,ExecuteNonQuery要求命令具有事务.该命令的Transaction属性尚未初始化".
我可以以任何方式使用挂起的事务登记我的SqlCommand cmd吗?我可以从IDbConnection对象中检索对待处理事务的引用(如果需要,我很乐意使用反射)?
如果有人对反射代码感兴趣来完成这个,那么它在这里:
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; }
笔记:
类型是内部的,属性是私有的,因此您不能使用动态
内部类型也会阻止您像第一个ConnectionInfo那样声明中间类型.必须在对象上使用GetType
哇我一开始并不相信.我很惊讶CreateCommand()
在使用本地SQL Server事务时没有给它命令它的事务,并且事务没有在SqlConnection
对象上公开.实际上,当反映SqlConnection
当前事务时甚至不存储在该对象中.在下面的编辑中,我给了你一些提示,通过他们的一些内部类来追踪对象.
我知道你不能修改方法但是你可以在方法栏周围使用TransactionScope吗?所以如果你有:
public static void CallingFooBar() { using (var ts=new TransactionScope()) { var foo=new Foo(); foo.Bar(); ts.Complete(); } }
这将有效,我使用类似的代码测试你的,一旦我添加包装所有工作正常,如果你可以这样做当然.正如所指出的那样,如果在TransactionScope
您内部打开了多个连接,您将被升级为分布式事务,除非为您配置系统,否则您将收到错误消息.
加入DTC也比本地交易慢几倍.
编辑如果你真的想尝试使用反射,SqlConnection有一个SqlInternalConnection,它依次有一个AvailableInternalTransaction属性,它返回一个SqlInternalTransaction,它有一个Parent属性,它返回你需要的SqlTransaction.