Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/277.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当用户取消查询时,如何回滚SQL CLR存储过程?_C#_Sql Server_Sqlclr - Fatal编程技术网

C# 当用户取消查询时,如何回滚SQL CLR存储过程?

C# 当用户取消查询时,如何回滚SQL CLR存储过程?,c#,sql-server,sqlclr,C#,Sql Server,Sqlclr,我正在尝试创建一个SQL CLR存储过程,该过程将创建一个表,将表名传递给一个服务,该服务将向其中批量插入一些数据,显示表的结果,然后清理表 到目前为止,我所尝试的: 使用SqlTransaction。取消事务是可行的,但它会使我的查询窗口处于无法继续处理的状态 此会话中活动的事务已由另一个会话提交或中止 使用TransactionScope。与1相同的问题 通过发出DROP tableSqlCommand手动清理finally子句中的表。虽然在发出命令之前mySqlContext.Pipe

我正在尝试创建一个SQL CLR存储过程,该过程将创建一个表,将表名传递给一个服务,该服务将向其中批量插入一些数据,显示表的结果,然后清理表

到目前为止,我所尝试的:

  • 使用
    SqlTransaction
    。取消事务是可行的,但它会使我的查询窗口处于无法继续处理的状态

    此会话中活动的事务已由另一个会话提交或中止

  • 使用
    TransactionScope
    。与1相同的问题

  • 通过发出
    DROP table
    SqlCommand
    手动清理
    finally
    子句中的表。虽然在发出命令之前my
    SqlContext.Pipe.Send()
    会运行此命令,但它似乎没有运行。它似乎与任何时间限制都没有关系,因为如果我在打印另一行之前发出
    Thread.Sleep(2000)
    ,它仍然会打印第二行,而
    命令.ExecuteNonQuery()
    会在打印第二行之前停止

  • 将手动清理代码放入CER或
    安全手柄
    。这不起作用,因为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)回滚事务
  • 需要执行上述工作流程。这可以通过创建一个包装器T-SQL存储过程来实现,该存储过程执行这3个步骤,并且只对包装器存储过程授予
    EXECUTE
    权限。如果SQLCLR存储过程需要权限,则可以使用模块签名轻松完成:

  • 在与SQLCLR存储过程相同的数据库中创建非对称密钥
  • 从该非对称密钥创建用户
  • 授予该基于密钥的用户对SQLCLR存储过程的
    执行
    权限
  • 使用该非对称密钥,使用
    addsignature
    ,对包装器T-SQL存储过程进行签名
  • 这4个步骤允许T-SQL包装程序执行SQLCLR程序,而实际应用程序登录只能执行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;
                }
    
            }
    
        }
    }