zoukankan      html  css  js  c++  java
  • 对delegate进行扩展 打造通用的"计时完成"方法 z

    让用户尽量少打字

            每次让用户输入这么多信息的确很糟糕, 可以改进一下设计: 服务器IP和用户名可以存放在配置文件里面, 初始化的时候默认加载到相应的文本框中; 从安全角度考虑, 密码必须经过用户手动输入; 而数据库名字则没必要让用户输入, 有了服务器IP、用户名、密码后可以尝试连接SQL Server, 连接SQL Server成功后, 把数据库中所有的数据库名加载到ComboBox让用户选择连接哪个数据库.

    密码不正确

    密码正确

    如何实现

            在后台代码中定义一个计时器, 设置它的Interval为1000毫秒, 用户输入密码时让定时器重新计时, 也就是说用户输入密码后, 如果在1秒钟内用户没有继续输入密码, 则会触发计时器的Elapsed事件, 这时程序尝试能不能连上SQL Server.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //用户停止输入密码1秒后自动尝试连接
    private System.Timers.Timer m_timer=new System.Timers.Timer(1000);
             
    private void txtPassword_PasswordChanged(object sender, RoutedEventArgs e)
    {
        m_timer.Stop();
        m_timer.Start();
     
        //清空数据库的选择列表
        cbbDatabase.Items.Clear();
    }

            在计时器的Elapsed事件函数中, 程序尝试连接SQL Server, 但是如下写法会出现问题.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    private void timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        Dispatcher.BeginInvoke((Action)delegate()
        {
            var conStr = String.Format("Data Source={0};Integrated Security=False;User ID={1};Password={2};", txtServer.Text, txtUser.Text, txtPassword.Password);
            using (var con = new SqlConnection(conStr))
            {
                try
                {
                    con.Open();
                }
                catch
                {
                    //TODO: 提示连接数据库失败
                    return;
                }
                //TODO:提示连接数据库成功
     
                var sql = "select name from sys.databases";
                var cmd = new SqlCommand(sql, con);
                IAsyncResult asyncResult = cmd.BeginExecuteReader();
                SqlDataReader reader = cmd.EndExecuteReader(asyncResult);
                if (!reader.HasRows)
                {
                    //TODO: 提示SQL Server中不存在数据库
                    return;
                }
                //TODO: 在这里释放Timer
     
                while (reader.Read())
                {
                    cbbDatabase.Items.Add(reader[0].ToString());
                }
            }
        });
    }

             如果数据库的IP是错误的, con.Open()则要花费约30秒左右才会抛出异常, 这段时间UI线程会一直卡死, 经过测试, 30秒后才抛异常与数据库连接字符串的Connect Timeout属性没有关系.

             可以写一个SqlConnection的扩展方法, 在这个方法里面用一个码表对执行con.Open()的时间计时, 用一个bool型变量标识有没有成功的con.Open(), 如果在规定的时间内没有成功连上SQL Server则抛出一个异常.

    扩展SqlConnection

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public static class SqlExtension
    {
        public static void TryOpen(this SqlConnection connection, int millisecondTimeout)
        {
            Stopwatch sw = new Stopwatch();
            bool succeed = false;
     
            Thread t = new Thread(() =>
                {
                    try
                    {
                        sw.Start();
                        connection.Open();
                        //打开连接后设置succeed为true
                        succeed = true;
                    }
                    catch
                    {
                    }
                });
            t.IsBackground = true;
            t.Start();
            t.Join(millisecondTimeout);
            t.Abort();
     
            //如果没有成功, 则抛出一个异常
            if (!succeed)
                throw new Exception();
        }
    }

            这样在原来的代码中调用con.TryOpen()就可以解决问题.

    扩展delegate

            其实扩展方法的第一个参数并不是一定要是一个class类, 其实delegate也是可以的, delegate其实在编译之后就是一个类, 如下改写刚刚的SqlConnection扩展方法, 就可以写一个通用的尝试操作的扩展方法.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    public static class ActionExtension
    {
        public static void Invoke(this Action action, int millisecondTimeout)
        {
            Stopwatch sw = new Stopwatch();
            bool succeed = false;
     
            Thread t = new Thread(() =>
                {
                    try
                    {
                        sw.Start();
                        action();
                        succeed = true;
                    }
                    catch
                    {
                    }
                });
            t.IsBackground = true;
            t.Start();
            t.Join(millisecondTimeout);
     
            if (!succeed)
                throw new Exception();
        }
    }

            这样写, 代码则更为通用一些, 以后一些耗时的操作(当然要满足Action的签名)都可以调用这个方法, 在本例中, 可以如下调用:

    1
    ((Action)con.Open).Invoke(2000);
  • 相关阅读:
    【转载】10个Web3D可视化精彩案例
    基于react的audio组件
    如何开发一款堪比APP的微信小程序(腾讯内部团队分享)
    CSS3 用border写 空心三角箭头 (两种写法)
    浅谈微信小程序对于创业者,意味着什么?
    左手Cookie“小甜饼”,右手Web Storage
    css3中user-select的用法详解
    个人感觉一些比较有用的特效例子
    纯css模拟电子钟
    蓝桥杯 ALGO-2:最大最小公倍数
  • 原文地址:https://www.cnblogs.com/zeroone/p/3867611.html
Copyright © 2011-2022 走看看