C# 当用户取消查询时,如何回滚SQL CLR存储过程?
我正在尝试创建一个SQL CLR存储过程,该过程将创建一个表,将表名传递给一个服务,该服务将向其中批量插入一些数据,显示表的结果,然后清理表 到目前为止,我所尝试的:C# 当用户取消查询时,如何回滚SQL CLR存储过程?,c#,sql-server,sqlclr,C#,Sql Server,Sqlclr,我正在尝试创建一个SQL CLR存储过程,该过程将创建一个表,将表名传递给一个服务,该服务将向其中批量插入一些数据,显示表的结果,然后清理表 到目前为止,我所尝试的: 使用SqlTransaction。取消事务是可行的,但它会使我的查询窗口处于无法继续处理的状态 此会话中活动的事务已由另一个会话提交或中止 使用TransactionScope。与1相同的问题 通过发出DROP tableSqlCommand手动清理finally子句中的表。虽然在发出命令之前mySqlContext.Pipe
SqlTransaction
。取消事务是可行的,但它会使我的查询窗口处于无法继续处理的状态
此会话中活动的事务已由另一个会话提交或中止
TransactionScope
。与1相同的问题
DROP table
SqlCommand
手动清理finally
子句中的表。虽然在发出命令之前mySqlContext.Pipe.Send()
会运行此命令,但它似乎没有运行。它似乎与任何时间限制都没有关系,因为如果我在打印另一行之前发出Thread.Sleep(2000)
,它仍然会打印第二行,而命令.ExecuteNonQuery()
会在打印第二行之前停止
安全手柄
。这不起作用,因为CER需要一些保证,包括不分配额外内存或调用未使用可靠性合同修饰的方法
[SqlProcedure]
public static void GetData(SqlString code)
{
Guid guid = Guid.NewGuid();
using (var connection = new SqlConnection("context connection=true"))
{
connection.Open();
try
{
SqlContext.Pipe?.Send("Constrain");
SqlCommand command1 = new SqlCommand($"CREATE TABLE qb.{code}_{guid:N} (Id INT)", connection);
command1.ExecuteNonQuery();
SqlContext.Pipe?.Send($"Create: qb.{code}_{guid:N}");
//emulate service call
Thread.Sleep(TimeSpan.FromSeconds(10));
SqlContext.Pipe?.Send($"Done: qb.{code}_{guid:N}");
}
finally
{
SqlContext.Pipe?.Send("1");
//drop table here instead of sleep
Thread.Sleep(2000);
SqlContext.Pipe?.Send("2");
}
}
}
不幸的是,SQLCLR不能很好地处理查询取消。但是,给出错误消息后,这似乎意味着取消操作会自行回滚。您是否尝试过不在SQLCLR代码中使用事务,而是从外部处理它?例如:
begintran代码>
EXEC SQLCLR\u存储过程代码>
如果(@@tracount>0)回滚事务代码>
EXECUTE
权限。如果SQLCLR存储过程需要权限,则可以使用模块签名轻松完成:
授予该基于密钥的用户对SQLCLR存储过程的执行权限
addsignature
,对包装器T-SQL存储过程进行签名回滚
之前中止执行,则当连接关闭时,事务应自动回滚
另外,您是否将XACT\u ABORT
设置为ON
或OFF
更新:O.P.声明它被设置为关闭
,而设置为打开
似乎没有任何不同
您是否尝试过在finally
块中检查连接状态?我很确定取消后,SqlConnection
已关闭。您可以在finally
块中尝试以下方法:
已关闭
,则重新打开SqlConnection
,然后执行non-query命令
更新:O.p.表示连接仍处于打开状态。好的,把它关上再重新打开怎么样
更新2:O.p.测试并发现无法重新打开连接SqlContext.Pipe.ExecuteAndSend(新的SqlCommand(“DROP TABLE..”)的东西)代码>
更新:O.p.声明此操作无效
##TableName
),该表a)可用于批量导入过程,b)在连接完全关闭时自行清理。在这种方法中,您在技术上不需要执行任何手动清理
当然,启用连接池时,只有在重新打开连接并执行第一个命令后,才会进行自动清理。为了强制立即清理,您必须在禁用连接池的情况下连接到SQL Server。是否可以仅在执行此存储过程时使用不同的连接字符串,其中包括Pooling=false代码>?鉴于此存储过程的使用方式,您似乎不会因为仅在这一特定调用上禁用连接池而遭受任何明显的性能下降。为了更好地理解连接池(启用或禁用)如何影响临时对象的自动清理,请参阅我刚刚发布的博客文章,其中详细介绍了这种行为:
总体而言,这种方法可能是最好的,因为您可能无法保证执行ROLLBACK
(前面提到的第一种方法)或执行finally子句(假设您曾经使用过)。最后,未提交的事务将回滚,但是如果有人通过SSMS执行此操作,并且它在没有回滚的情况下中止,那么他们仍然处于打开的事务中,并且可能没有意识到它。阿尔索
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Threading;
static class SqlConnectionExtensions
{
public static DataTable ExecuteDataTable(this SqlConnection con, string sql, params SqlParameter[] parameters)
{
var cmd = new SqlCommand(sql, con);
foreach (var p in parameters)
{
cmd.Parameters.Add(p);
}
using (var dr = cmd.ExecuteReader())
{
var dt = new DataTable();
dt.Load(dr);
return dt;
}
}
public static int ExecuteNonQuery(this SqlConnection con, string sql, params SqlParameter[] parameters)
{
var cmd = new SqlCommand(sql, con);
foreach (var p in parameters)
{
cmd.Parameters.Add(p);
}
return cmd.ExecuteNonQuery();
}
}
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void GetData(SqlString code)
{
Guid guid = Guid.NewGuid();
using (var connection = new SqlConnection("context connection=true"))
{
connection.Open();
try
{
connection.ExecuteNonQuery("begin transaction;");
SqlContext.Pipe?.Send("Constrain");
connection.ExecuteNonQuery($"CREATE TABLE qb.{code}_{guid:N} (Id INT)");
SqlContext.Pipe?.Send($"Create: qb.{code}_{guid:N}");
//emulate service call
Thread.Sleep(TimeSpan.FromSeconds(10));
SqlContext.Pipe?.Send($"Done: qb.{code}_{guid:N}");
connection.ExecuteNonQuery("commit transaction");
}
catch (Exception ex)
{
connection.ExecuteNonQuery("rollback;");
throw;
}
}
}
}