为什么要检测数据库连接是否可用,项目经理说如果实现了这个功能,客户会认可你的软件很专业,很好用
前几天,有园友针对我的文章《工作多年后积累的设计灵活,稳定,优秀WinForms应用程序的最佳实践》提出一个问题,说数据库的应用程序,在保存数据到SQL Server中去的时候,突然发现数据库服务器不可用,抛出这个异常
如果用户很辛苦的输入了很多数据,点击保存时,才发现服务器连接不可用。这时用户肯定会很恼火,如果你是用户,你也会觉得很冤枉,辛苦输入的数据又不能保存,你干吗不早说呢,早点告诉我服务器不可用,我也可以不用白费功夫。
今天的文章就是为解决这个问题,如何检测SQL Server 服务是否可用,可以连接,以保证任何时候,用户输入数据的工作量不白费,节约客户的时间,如果发现SQL Server服务器不可用,要马上通知用户,阻止用户继续输入数据。做到软件是为解决问题,而不是增加麻烦。
我想到的第一个办法,是在保存数据的时候,检测服务器可否连接,这是必须的,代码如下
public static bool CheckConnectionAvailableBySql() { SqlConnection conn = new SqlConnection(ConnectionString); try { conn.Open(); return true; } catch (Exception ex) { return false; } }
但是,我们要达到的目标不是在保存数据时检测,而是用户打开程序后,随时要检测,于是我想到这样,再加一个Timer控件,在它的Tick事件中轮循检测数据库服务是否可用
private void timer_Tick(object sender, EventArgs e) { bool available = Program.CheckConnectionAvailableBySql(); if (available) { Console.WriteLine("server is available"); } }
再把这个封装成BaseForm窗体基类,各种的业务单据输入窗体都继承于这个窗体,这样就实现了在用户打开窗体,输入数据时,随时可以检测数据库连接是否可用。
如果你觉得用SqlConnection会造成对SQL Server服务的性能有影响,可以考虑用网络Ping命令,再配合ServiceProcess对象模型,以提高性能。请看下面的实现代码
private static bool CheckConnectionAvailableByNetwork() { bool available = false; SqlConnectionStringBuilder conection = new SqlConnectionStringBuilder(ConnectionString); string machineName = conection.DataSource; Ping ping = new Ping(); PingReply reply = ping.Send(machineName, 4000); if (reply.Status == IPStatus.Success) { ServiceController[] AvailableServices = ServiceController.GetServices(machineName); foreach (ServiceController AvailableService in AvailableServices) { if (AvailableService.ServiceName.Equals("MSSQLSERVER", StringComparison.InvariantCultureIgnoreCase)) { if (AvailableService.Status == ServiceControllerStatus.Running) { available = true; break; } } } } return available; }
这个方法是检测指定的机器的SQL Server服务是否可用,那一句foeach循环遍历改成Linq效率会更好一些。
留言的园友提供了另一个实现思路,用网络连接方式,判断SQL Server是否可用,再把它发送到应用程序中。这个思路在我的ERP Solution已经实现了,现在把它分享出来,供大家参考。
为实现这个功能,分两个模块,Check SQL Server Available是服务器检测程序,检测SQL Servver是否可用。把它单独放到一个进程中,Main Form是应用程序,把它理解为客户端程序,用于输入数据,保存到SQL Server中。把检测SQL Server服务器是否可用放到一个单独的进程中,是为了节省Main Form程序的资源,我认为用Timer来轮循SQL Server是否可用,会消耗一些内存和CPU,独立出来,Main Form的性能会好一些。
上面给出的代码,CheckConnectionAvailableBySql/CheckConnectionAvailableByNetwork和timer_Tick,就是Check SQL Server Available的代码,下面来分析它如何把SQL Server服务器是否可用的信息发送到Main Form应用程序。
这里使用的是Socket通信,Check SQL Server Available检测SQL Servver是否可用,如果可用,发送S字母到Main Form中,不可用则发送F字母到Main Form中,Main Form根据收到的消息做出处理。如果收到F消息,表示SQL Server已经不可用,马上停止当前的工作,让界面hang-on,效果是这样的
我在命令行窗口中敲入net stop mssqlserver以停止当前的SQL Server服务。Check SQL Serve Available的timer_Tick事件检测到SQL Server服务器已经被停掉,于是发送F到Main Form应用程序,Main Form收到这个字母信号,知道SQL Server服务不可用,于是挂起当前的窗体,显示Trying to conncet to the server。
再次,我在命令行中输入net start mssqlserver以启动SQL Serve。当Check SQL Server Available检测到之后,会发送S到Main Form中,Main Form知道SQL Server可用,于是恢复当前的界面,允许用户操作。
为节省网络带宽,Main Form只接受了Check SQL Server Available发送的一个字母,S表示成功连接到SQL Serve服务器,successfully,F表示连接失败,failed。
来分析一下源代码的实现,检测SQL Server是否可用的代码,现在加多了一行发送状态的语句
private void timer_Tick(object sender, EventArgs e) { bool available = Program.CheckConnectionAvailableBySql(); if (available) { Program.OnSendData("S"); //succesfully Console.WriteLine("server is available"); } else Program.OnSendData("F"); //failed }
进入到OnSendData方法中,这是标准的Socket 发送字符串的代码,参考如下
public static void OnSendData(string message) { try { Object objData = message; byte[] byData = System.Text.Encoding.ASCII.GetBytes(objData.ToString()); m_socClient.Send(byData); } catch (SocketException e) { //Console.WriteLine(e.ToString()); } }
在窗体加载的Load方法中,启动Socket连接程序,以连接到Main Form的socket接口。
public static void EstablishSocketServer() { OnConnect("127.0.0.1", 8221); }
在Main Form窗体这边,根据接收到的字符的不同,表现不同的状态。代码参考如下
if (!command.StartsWith("S")) { TryToConnection dlg = Application.OpenForms["TryToConnection"] as TryToConnection; if (dlg == null || dlg.IsDisposed) dlg = new TryToConnection(); dlg.Visible = false; dlg.Show(this); this.Enabled = false; } else { TryToConnection dlg = Application.OpenForms["TryToConnection"] as TryToConnection; if (dlg != null) { dlg.Visible = false; dlg.Dispose(); dlg = null; } this.Enabled = true; }
关于Socket的介绍,请参考MSDN,在这篇文章中,它的目的是用来传送SQL Server的可用状态。如果你熟悉TCP或是UDP的接口,这一部分也可以替换成你熟悉的网络方式。
请到epn.codeplex.com下载这篇文章的完整代码,代码名称是SQL Server Available Check。