让用户尽量少打字

每次让用户输入这么多信息的确很糟糕, 可以改进一下设计: 服务器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);

对delegate进行扩展 打造通用的"计时完成"方法 z的更多相关文章

  1. RBAC打造通用WEB权限

    RBAC不用给用户单个分配权限,只用指向对应的角色就会有对应的权限,而且分配权限和收回权限都很方便 5个关系对应5张表 五张表设计 CREATE TABLE `user` ( `id` ) unsig ...

  2. 为RecyclerView打造通用Adapter

    ##RecycleView简单介绍 RecyclerView控件和ListView的原理有非常多相似的地方,都是维护少量的View来进行显示大量的数据.只是RecyclerView控件比ListVie ...

  3. Android教你怎样一步步打造通用适配器

    前言 在Android开发中ListView是最为经常使用的控件之中的一个,基本每一个应用都会涉及到它,要使用ListView列表展示,就不可避免地涉及到另外一个东西--Adapter,我们都知道,A ...

  4. 为RecyclerView打造通用Adapter 让RecyclerView更加好用

    原文出处: 张鸿洋 (Granker,@鸿洋_ ) 一.概述 记得好久以前针对ListView类控件写过一篇打造万能的ListView GridView 适配器,如今RecyclerView异军突起, ...

  5. 转载扩展Windows Mobile模拟器存储空间的方法

    扩展Windows Mobile模拟器存储空间的方法 在Windows Mobile应用程序开发的初期,可以使用SDK自带的模拟器来进行调试,这给我们开发人员提供了一种方便的途径.一般的应用程序,占用 ...

  6. delphi附带通用控件安装方法:

    附带通用控件安装方法:----------基本安装1.对于单个控件,Componet-->install component..-->PAS或DCU文件-->install;2.对于 ...

  7. 通用Mapper的各个方法描述,参考官方

    下面是通用Mapper的各个方法描述,主要还是看官方的描述https://mapperhelper.github.io/all/. 基础接口 Select 接口:SelectMapper<T&g ...

  8. 关于visual studio code在win10系统上安装后会报扩展宿主意外终止的解决方法

    我的电脑的地址 C:\Users\Administrator.SC-201810160958\AppData\Local\Programs\Microsoft VS Code\resources\ap ...

  9. jQuery通用的全局遍历方法$.each()用法实例

    1.jQuery通用的全局遍历方法$.each()用法 2. test.json文件代码: 3. html代码 4.jQuery代码 <script src="jquery-1.3.1 ...

随机推荐

  1. HDU 3461 Code Lock(并查集,合并区间,思路太难想了啊)

    完全没思路,题目也没看懂,直接参考大牛们的解法. http://www.myexception.cn/program/723825.html 题意是说有N个字母组成的密码锁,如[wersdfj],每一 ...

  2. POJ 1906

    #include <iostream> #include <string> #define MAXN 150 using namespace std; int _m[MAXN] ...

  3. 李洪强iOS开发之【Objective-C】08-self关键字

    一.Java中的this只能用在动态方法中,不能用在静态方法中 1.在动态方法中使用this关键字 1 public class Student { 2 private int age; 3 publ ...

  4. 计算XX年的某月某日是当年的第多少天?是星期几?

    /** * 编写程序,计算2013年的某月某日是当年的第多少天?是星期几?已知2013年元旦是星期二. */ import java.util.Scanner; public class Date { ...

  5. IDEA15 File工具栏中没有 Import Project

    使用IDEA准备导入项目时发现没有Import Project选项... 解决办法: Settings > Appearance & Bechavior > Menus and T ...

  6. kafka的环境搭建

    kafka是一个高吞吐量的消息系统.隔离消息接收和处理过程(可理解为一个缓存) 1.kafka伪分布的部署 1.1.下载并解压 1.2.启动zk bin/zookeeper-server-start. ...

  7. NDK(16)Jni中GetStaticFieldID和GetMethodID 中的类型标识串

    env在GetStaticFieldID和GetMethodID 时,函数参数和返回值的类型要指定类型标识串,如: jmethodID init = env->GetMethodID(clz,& ...

  8. C# 对象与JSON串互相转换

    using System;using System.IO;using System.Text;using Newtonsoft.Json; namespace OfflineAcceptControl ...

  9. Proxifier设置代理

    1.首先需要开启http代理选项---配置文件->高级->HTTP代理服务器,勾选“启用HTTP代理服务器支持” 2.然后开始添加代理服务器选择“配置文件->代理服务器”,在弹出框点 ...

  10. Ubuntu中MySQL中文乱码解决

    1.以root登陆,在终端输入命令 sudo gedit /etc/mysql/my.cnf在打开的文件中找到[client]在下面加入default-character-set=utf8 找到 [m ...