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

            连接数据库是程序员经常要做的事情, 通常情况下, 需要连接SQL Server中哪个数据库是已知的, 用户在输入SQL Server的服务器IP、用户名、密码和数据库名字后, 如果所有信息输入正确, 就可以建立连接了.

    让用户尽量少打字

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

    密码不正确

    密码正确

    如何实现

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

    //用户停止输入密码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, 但是如下写法会出现问题.

    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

    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扩展方法, 就可以写一个通用的尝试操作的扩展方法.

    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的签名)都可以调用这个方法, 在本例中, 可以如下调用:

    ((Action)con.Open).Invoke(2000);

    作者:Create Chen
    出处:http://technology.cnblogs.com
    说明:文章为作者平时里的思考和练习,可能有不当之处,请博客园的园友们多提宝贵意见。
    知识共享许可协议本作品采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可。

  • 相关阅读:
    (转)正则表达式与Python(RE)模块
    (转)【面试】【MySQL常见问题总结】【03】
    (转)MySQL性能调优my.cnf详解
    (转)python logging模块
    (转)python collections模块详解
    mysql故障总结
    rocksdb 编译安装 日志
    c++11 gcc4.8.x安装
    Install ssdb-rocks on CentOS 6
    在Shell里面判断字符串是否为空
  • 原文地址:https://www.cnblogs.com/technology/p/3018457.html
Copyright © 2011-2022 走看看