zoukankan      html  css  js  c++  java
  • 如何取消 SqlDataAdapter.Fill() 的执行(转载)


    Scenario: We have a DataGridView which is attached to DataAdapter (datatable), we load the data in datatable using (adapter.fill(query, datatable)) in a separate thread (using delegate and beginInvoke) and once the data is loaded we attached that datatable to datagridview (in the main thread)
    Is there a way we can check if fill() is still executing and cancel it.

    Real scenario: User click on the user name and corresponding data is loaded in the datagrid. Sometime, user is impatient and click on the another user (here I want to cancel the previous fill and start a new fill)

    UPDATE: We keep two DataApdaters (and two DataTables) and we attach one datatable to datagridview and start loading data to another datatable asynchronously. When data is loaded we simply bind the datagridview to DataTable which we just filled (and start loading the previous datable asynchronously) This way UI will always get the current data (without user waiting on UI to refresh or hang)


    You can provide a SqlCommand to adapter constructor and invoke a Cancel method on it. There is a raw template :

    class Model 
    {
        private SqlCommand loadUserCommand;
        private DataTable userData;
    
        public void LoadUser(string userId) 
        {
            loadUserCommand = GetUserLoadCommandForUserID(userId);
            userData = new DataTable("userData");
            using (var adapter = new SqlDataAdapter(loadUserCommand)) 
            {
                adapter.Fill(userData);
            }
        }
    
        public void AbortLoadUser()
        {
            if (loadUserCommand!= null)
                loadUserCommand.Cancel();
        }
    
    
        private SqlCommand GetUserLoadCommandForUserID(string userId)
        {
            var connection = new SqlConnection("...");
            var command = connection.CreateCommand();
            ...
        }
    }

    注意,在执行SqlCommand.Cancel()方法时,如果SqlDataAdapter.Fill方法还没有执行完毕,那么SqlDataAdapter.Fill方法会抛出SqlException异常,所以为了安全起见,我们应该在代码中包含异常捕获和处理逻辑,如下所示:

    class Model
    {
        private SqlCommand loadUserCommand;
        private DataTable userData;
    
        public void LoadUser(string userId)
        {
            loadUserCommand = GetUserLoadCommandForUserID(userId);
            userData = new DataTable("userData");
    
            try
            {
                using (var adapter = new SqlDataAdapter(loadUserCommand))
                {
                    adapter.Fill(userData);
                }
            }
            catch (SqlException ex)
            {
                //异常处理逻辑
            }
        }
    
        public void AbortLoadUser()
        {
            if (loadUserCommand != null)
                loadUserCommand.Cancel();
        }
    
    
        private SqlCommand GetUserLoadCommandForUserID(string userId)
        {
            var connection = new SqlConnection("...");
            var command = connection.CreateCommand();
            ...
        }
    }

    同理,SqlCommand.Cancel()方法也可以用来取消SqlDataReader的执行,如下所示:

    using Microsoft.Data.SqlClient;
    using System;
    using System.Data;
    using System.Threading.Tasks;
    
    namespace NetCoreADOTesting
    {
        class Program
        {
            static SqlCommand currentCommand = null;
    
            public static async Task RunSql()
            {
                int row = 0;
    
                try
                {
                    string strConn = "Data Source=192.168.1.1;Initial Catalog=TestDB; User Id=sa;Password=TUI123456";
                    string sql = @"SELECT [ID]
                                      ,[Name]
                                      ,[Age]
                                  FROM [dbo].[People];
                                  waitfor delay '1:00';
                                  ";
    
                    using (SqlConnection sqlConnection = new SqlConnection(strConn))
                    {
                        sqlConnection.Open();
    
                        using (SqlCommand sqlCommand = new SqlCommand(sql, sqlConnection))
                        {
                            currentCommand = sqlCommand;
                            sqlCommand.CommandTimeout = 0;
    
                            using (SqlDataReader sqlDataReader = currentCommand.ExecuteReader())
                            {
                                if (sqlDataReader.HasRows)
                                {
                                    while (sqlDataReader.Read())
                                    {
                                        //有数据从SqlDataReader中读到
                                        row++;
                                    }
                                }
                            }
                        }
                    }
    
                    Console.WriteLine("SqlDataReader row count is {0}", row);
                }
                catch (SqlException ex)
                {
                    //异常处理逻辑
                    Console.WriteLine(ex.ToString());
                }
    
                await Task.CompletedTask;
            }
    
            static void Main(string[] args)
            {
                Task t = Task.Run(async () =>
                  {
                      await RunSql();
                  });
    
                Console.WriteLine("Press any key to cancel...");
                Console.ReadKey();
    
                if (currentCommand != null)
                {
                    currentCommand.Cancel();
                }
    
                Console.WriteLine("Press any key to end...");
                Console.ReadKey();
            }
        }
    }

    这里有一点需要注意,如果SqlCommand.Cancel()方法在生成SqlDataReader之前就被调用了(上面黄色代码行之前),是不会起作用的, SqlDataReader还是会被执行,除非再次调用SqlCommand.Cancel()方法,SqlDataReader的执行才会被取消,并抛出SqlException异常。也就是说SqlCommand.Cancel()方法一定要在SqlDataReader执行之后调用,才能取消SqlDataReader的执行。同样,SqlCommand.Cancel()方法如果在SqlDataAdapter.Fill方法之前执行,也是不起作用的,这是SqlCommand.Cancel()方法的一个缺陷,因为调用SqlCommand.Cancel()方法时,我们并不知道SqlDataReader和SqlDataAdapter是否正在执行。

    因此最好的办法还是用SqlCommand.ExecuteReaderAsync(CancellationToken cancellationToken)方法和SqlDataReader.ReadAsync(CancellationToken cancellationToken)方法,结合CancellationToken参数来取消执行。注意,如果CancellationToken参数被取消,ExecuteReaderAsyncReadAsync方法会抛出TaskCanceledException异常,同样我们在代码中要包含异常捕获和处理逻辑,如下所示:

    using Microsoft.Data.SqlClient;
    using System;
    using System.Data;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace NetCoreADOTesting
    {
        class Program
        {
            static CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    
            public static async Task RunSql()
            {
                int row = 0;
    
                try
                {
                    string strConn = "Data Source=192.168.1.1;Initial Catalog=TestDB; User Id=sa;Password=TUI123456";
                    string sql = @"SELECT [ID]
                                      ,[Name]
                                      ,[Age]
                                  FROM [dbo].[People];
                                  waitfor delay '1:00';
                                  ";
    
                    using (SqlConnection sqlConnection = new SqlConnection(strConn))
                    {
                        sqlConnection.Open();
    
                        using (SqlCommand sqlCommand = new SqlCommand(sql, sqlConnection))
                        {
                            sqlCommand.CommandTimeout = 0;
    
                            using (SqlDataReader sqlDataReader = await sqlCommand.ExecuteReaderAsync(cancellationTokenSource.Token))
                            {
                                if (sqlDataReader.HasRows)
                                {
                                    while (await sqlDataReader.ReadAsync(cancellationTokenSource.Token))
                                    {
                                        //有数据从SqlDataReader中读到
                                        row++;
                                    }
                                }
                            }
                        }
                    }
    
                    Console.WriteLine("SqlDataReader row count is {0}", row);
                }
                catch (TaskCanceledException ex)
                {
                    //异常处理逻辑
                    Console.WriteLine(ex.ToString());
                }
            }
    
            static void Main(string[] args)
            {
                Task t = Task.Run(async () =>
                {
                    await RunSql();
                });
    
                Console.WriteLine("Press any key to cancel...");
                Console.ReadKey();
    
                if (cancellationTokenSource != null)
                {
                    cancellationTokenSource.Cancel();
                }
    
                Console.WriteLine("Press any key to end...");
                Console.ReadKey();
            }
        }
    }

    这样,只要我们执行了CancellationTokenSource.Cancel()方法,那么SqlCommand.ExecuteReaderAsync(CancellationToken cancellationToken)方法和SqlDataReader.ReadAsync(CancellationToken cancellationToken)方法都会被取消执行,并抛出TaskCanceledException异常。

      

    原文链接

  • 相关阅读:
    Ceph rbd删除image遭遇watchers异常处理
    Ceph OSD更换硬盘后遭遇PG Inconsistent异常与处理
    Rook Ceph OSD异常,格式化osd硬盘重新挂载
    Count on an IEnumerable<dynamic>
    [原创] [C#] 转换Excel数字列号为字母列号
    [MAC] Load Crypto.Cipher.ARC4 Failed, Use Pure Python Instead.
    转:Chrome调试工具介绍
    转:一组jQuery插件的连接
    动态的链式传递执行
    C#中克隆随机数的三种方法(为什么我想到了茴字的四种写法
  • 原文地址:https://www.cnblogs.com/OpenCoder/p/12063867.html
Copyright © 2011-2022 走看看