我们开始演化出最简单的智能客户端TaskVision。从上一篇讲述的Projects项目表开始吧。
先看下我们完成后的效果:
先谈谈界面吧。
控件:
MenuStrip: mainMenu 使用插入标准项
ToolStrip:toolBar 使用插入标准项
XPanderList:xpList
XPander:xpProject
相信大家已经注意到了左边仿Windows效果的控件,在TaskVision中你可以找到:XPanderControl.dll。
添加到工具箱可以看到两个自定义控件:
我们在xpProject控件中拉入两个控件:
Lable:lblSelectProject
ComboBox:cbProjects
(一)、使用上一篇已经构建好的Web服务和强类型DataSet:DataSetProjects
Web服务:DataService.asmx
代码如下:
using System.Data;
using System.Data.SqlClient;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
/// <summary>
/// DataService 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class DataService : System.Web.Services.WebService
{
private SqlConnection _conn;
private SqlDataAdapter _daProjects;
private SqlCommand _selectProjectsCommand;
public DataService()
{
//如果使用设计的组件,请取消注释以下行
//InitializeComponent();
_conn = new SqlConnection("Data Source=(local);Initial Catalog=TaskVision;User ID=sa;Password=password;");
_daProjects = new SqlDataAdapter();
_selectProjectsCommand = new SqlCommand();
_selectProjectsCommand.Connection = _conn;
_selectProjectsCommand.CommandText = "SELECT ProjectID, ProjectName, ProjectDescription, DateCreated FROM Projects WHERE (IsDeleted = 0)";
_daProjects.SelectCommand = _selectProjectsCommand;
}
[WebMethod]
public DataSetProjects GetProjects()
{
DataSetProjects ds = new DataSetProjects();
_daProjects.Fill(ds.Projects);
return ds;
}
}
(二)、
在本地应用程序中引用该Web服务:名称为DataWS
主窗体:MainForm
{
InitializeComponent();
DataWS.DataService dataService = new DataWS.DataService();//Web服务代理
this.Cursor = Cursors.WaitCursor;
cbProjects.DataSource = dataService.GetProjects().Projects.DefaultView;
cbProjects.DisplayMember = "ProjectName";
cbProjects.ValueMember = "ProjectID";
this.Cursor = Cursors.Default;
}
到这里我们已经完成了一个最简单的智能客户端。
不过上面的代码有很多缺陷,比如异常处理等等。
小菜将开始演化代码了。(注意,小菜写出的代码可能会与TaskVision中的源码有些不同,这完全是个人喜好问题,两个不一样的人怎么可能写出一样的代码,而且小菜比较喜欢IssueVision中的代码风格,相信聪明的你会对比,选择你喜欢的。)
我们新建一个DataLayer类来初步封装对Web服务GetProjects方法的调用。
using TaskVision.DataWS;
namespace TaskVision
{
public enum WebServicesExceptionType
{
WebException = 0,
SoapException = 1,
Exception = 2,
None = 3
}
public class DataLayer
{
private DataService _dataService = new DataService();
private DataSetProjects _dsProjects = new DataSetProjects();
public DataSetProjects.ProjectsDataTable Projects
{
get
{
return _dsProjects.Projects;
}
}
public DataLayer()
{
}
public WebServicesExceptionType GetProjects()
{
DataSetProjects dsProjects = null;
WebServicesExceptionType exceptionType = WebServicesExceptionType.None;
try
{
dsProjects = _dataService.GetProjects();
}
catch (System.Net.WebException)
{
exceptionType = WebServicesExceptionType.WebException;
}
catch (System.Web.Services.Protocols.SoapException)
{
exceptionType = WebServicesExceptionType.SoapException;
}
catch (System.Exception)
{
exceptionType = WebServicesExceptionType.Exception;
}
if (dsProjects != null)
{
_dsProjects.Clear();
_dsProjects.Merge(dsProjects);
}
return exceptionType;
}
}
}
其中有三个异常WebException:通常会在无法连接到服务器引发。SoapException:通常会在调用的Web服务中的Web方法中出现异常引用。Exception:未知异常。
我们现在需要一个思路,如果出现异常我们需要怎么处理呢?
我们可以在主窗体中使用DataLayer,调用GetProjects()方法,调用该方法的结果无非两种:成功,失败。成功那当然是最好了,如果失败怎么办?其一、我们需要给用户提示是出现什么错误。其二、我们应用给用户重试的机会。比如网络中断,将出现WebException错误,用户重新连接上网络,重试后,则可以正常使用该系统。
在主窗体中使用:
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace TaskVision
{
public partial class MainForm : Form
{
private DataLayer _dataLayer;
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
_dataLayer = new DataLayer();
this.Cursor = Cursors.WaitCursor;
try
{
if (!GetProjects())
{
throw new Exception("无法加载项目");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
this.Close();//关闭主窗体
return;
}
cbProjects.DataSource = _dataLayer.Projects.DefaultView;
cbProjects.DisplayMember = "ProjectName";
cbProjects.ValueMember = "ProjectID";
this.Cursor = Cursors.Default;
}
private enum ResultStatus
{
Success = 0,
Failure = 1,
Retry = 2
}
private ResultStatus CheckResult(WebServicesExceptionType exceptionType)
{
ResultStatus resultStatus = ResultStatus.Retry;
if (exceptionType == WebServicesExceptionType.None)
{
resultStatus = ResultStatus.Success;//成功
}
else if (exceptionType == WebServicesExceptionType.WebException)
{
DialogResult mbResult = MessageBox.Show("无法连接到服务器,请重试.", "错误", MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
if (mbResult != DialogResult.Yes)
resultStatus = ResultStatus.Failure;
}
else if (exceptionType == WebServicesExceptionType.SoapException)
{
DialogResult mbResult = MessageBox.Show("调用Web方法发生错误,请重试.", "错误", MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
if (mbResult != DialogResult.Yes)
resultStatus = ResultStatus.Failure;
}
else
{
resultStatus = ResultStatus.Failure;
}
return resultStatus;
}
private bool GetProjects()
{
do
{
WebServicesExceptionType exceptionType = _dataLayer.GetProjects();
//处理结果
switch (CheckResult(exceptionType))
{
case ResultStatus.Failure:
return false;
case ResultStatus.Success:
return true;
}
} while (true);
}
}
}
在GetProjects()方法中,我们使用了一个do{}while(true)循环,只到失败或成功才结束,如果CheckResult(exceptionType)是重试则一直循环下去。
上面的代码其实已经挺漂亮了,不过我们还是应该找出不足的地方。
(一)、下面这段代码
{
if (!GetProjects())
{
throw new ExitException("无法加载项目");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
this.Close();//关闭主窗体
return;
}
我们可以预测到上面显示异常的代码在主窗体中将会出现在很多地方,我们可以对其进行简单的封装.
(二)、我们直接在代码中指定了异常标题和异常内容:"无法连接到服务器,请重试." 等,这会给我们的维护代码带来麻烦,所以我们可以使用常量.
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace TaskVision
{
public partial class MainForm : Form
{
private DataLayer _dataLayer;
private const string TV_EXCEPTION_TITLE = "错误";
private const string TV_WEBEXCEPTION_TEXT = "无法连接到服务器,请重试.";
private const string TV_SOAPEXCEPTION_TEXT = "调用Web方法发生错误,请重试.";
//自定义异常类
private class ExitException : ApplicationException
{
private string _msg;
public ExitException()
{
}
public ExitException(string msg)
{
_msg = msg;
}
public void show()
{
if (_msg != null)
{
MessageBox.Show(_msg, TV_EXCEPTION_TITLE, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
_dataLayer = new DataLayer();
this.Cursor = Cursors.WaitCursor;
try
{
if (!GetProjects())
{
throw new ExitException("无法加载项目");
}
}
catch (ExitException ex)
{
ex.Show();
this.Close();//关闭主窗体
return;
}
cbProjects.DataSource = _dataLayer.Projects.DefaultView;
cbProjects.DisplayMember = "ProjectName";
cbProjects.ValueMember = "ProjectID";
this.Cursor = Cursors.Default;
}
private enum ResultStatus
{
Success = 0,
Failure = 1,
Retry = 2
}
private ResultStatus CheckResult(WebServicesExceptionType exceptionType)
{
ResultStatus resultStatus = ResultStatus.Retry;
if (exceptionType == WebServicesExceptionType.None)
{
resultStatus = ResultStatus.Success;//成功
}
else if (exceptionType == WebServicesExceptionType.WebException)
{
DialogResult mbResult = MessageBox.Show(TV_WEBEXCEPTION_TEXT, TV_EXCEPTION_TITLE, MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
if (mbResult != DialogResult.Yes)
resultStatus = ResultStatus.Failure;
}
else if (exceptionType == WebServicesExceptionType.SoapException)
{
DialogResult mbResult = MessageBox.Show(TV_SOAPEXCEPTION_TEXT, TV_EXCEPTION_TITLE, MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
if (mbResult != DialogResult.Yes)
resultStatus = ResultStatus.Failure;
}
else
{
resultStatus = ResultStatus.Failure;
}
return resultStatus;
}
private bool GetProjects()
{
do
{
WebServicesExceptionType exceptionType = _dataLayer.GetProjects();
//处理结果
switch (CheckResult(exceptionType))
{
case ResultStatus.Failure:
return false;
case ResultStatus.Success:
return true;
}
} while (true);
}
}
}
那就让我们来改造下DataLayer.
先修改app.config.将Web服务的Url地址依赖注入,方便我们以后更改Web服务的Url地址.
<configuration>
<appSettings>
<add key="DataServiceUrl" value="http://localhost/TaskVisionWS/DataService.asmx"/>
</appSettings>
</configuration>
using TaskVision.DataWS;
namespace TaskVision
{
public enum WebServicesExceptionType
{
WebException = 0,
SoapException = 1,
Exception = 2,
None = 3
}
public class DataLayer
{
private DataService _dataService;
private DataSetProjects _dsProjects = new DataSetProjects();
public DataSetProjects.ProjectsDataTable Projects
{
get
{
return _dsProjects.Projects;
}
}
public DataLayer()
{
_dataService = GetWebServiceReference();
}
public WebServicesExceptionType GetProjects()
{
DataSetProjects dsProjects = null;
WebServicesExceptionType exceptionType = WebServicesExceptionType.None;
try
{
dsProjects = _dataService.GetProjects();
}
catch (System.Net.WebException)
{
exceptionType = WebServicesExceptionType.WebException;
}
catch (System.Web.Services.Protocols.SoapException)
{
exceptionType = WebServicesExceptionType.SoapException;
}
catch (System.Exception)
{
exceptionType = WebServicesExceptionType.Exception;
}
if (dsProjects != null)
{
_dsProjects.Clear();
_dsProjects.Merge(dsProjects);
}
return exceptionType;
}
private DataService GetWebServiceReference()
{
DataService dataService = new DataService();
string urlSetting = System.Configuration.ConfigurationManager.AppSettings["DataServiceUrl"];
if (urlSetting != null)
{
dataService.Url = urlSetting;
}
else
{
dataService.Url = "http://localhost/TaskVisionWS/DataService.asmx";
}
return dataService;
}
}
}
(一)、先来测试WebException。怎么测呢?将IIS停止就可以了。这样我们的程序就无法访问到服务器了。
你可以点击否。则GetProjects()返回false,则抛出无法加载项目的异常。
你也可以启动IIS,点击是,则重试,则程序正常运行。
(二)、让我们来测试SoapException。怎么测呢?在调用的Web方法中抛出异常。
public DataSetProjects GetProjects()
{
DataSetProjects ds = new DataSetProjects();
_daProjects.Fill(ds.Projects);
throw new Exception("抛出异常,本地应用程序将引发SoapException");
return ds;
}