WCF分布式开发步步为赢(8):使用数据集、数据表、集合传递数据
[2] 集合(Collection)
[3] 示例代码分析
【3】示例代码分析:
下面我们来介绍一下使用Dataset、 Datatable和集合类来传递数据的程序开发过程。依次介绍服务契约、宿主、客户端的开发配置过程,另外服务端设计了一个数据库,添加了部分演示数据,目的是方便Demo。
【3.1】服务契约:
服务契约定义了3个操作契约,分别是使用Dataset、Datatable、List来传递数据,WCF服务类实现了接口定义的操作契约,分别返回不同的数据结构类型。具体代码如下:
//因此本例开头使用 using 语句来引用该命名空间。
//为了掩饰WCF服务的操作重载
namespace WCFService
{
//1.服务契约,操作契约重载
[ServiceContract(Namespace = "http://www.cnblogs.com/frank_xl/")]
interface IWCFService
{
//操作契约,数据表
[OperationContract]
System.Data.DataTable GetDataByTable();
//操作契约,数据集
[OperationContract]
System.Data.DataSet GetDataByDataSet();
//操作契约,数据集合
[OperationContract]
List<User> GetDataByCollection();
}
//2.服务类,集成接口。实现契约
public class WCFService : IWCFService
{
//实现接口定义的方法,DataTable传递数据
public System.Data.DataTable GetDataByTable()
{
//这里可以定义数据持久化操作,访问数据库等
System.Data.DataSet dataSet = new System.Data.DataSet();
System.Data.DataTable dataTable = null;
SqlConnection sqlConnection = new SqlConnection("Data Source=.\\SQL
EXPRESS;AttachDbFilename=|DataDirectory|\\Database\\DatabaseWCF.mdf;Integrated Security=True;User Instance=True");
try
{
System.Data.SqlClient.SqlDataAdapter sqlDataAdapter = new System.
Data.SqlClient.SqlDataAdapter("SELECT id, name, english_name FROM TableWCF",
sqlConnection);
sqlDataAdapter.Fill(dataSet, "TableWCF");
if (dataSet != null && dataSet.Tables.Count > 0)
{
dataTable = dataSet.Tables[0];
}
}
catch (Exception e)
{
}
finally
{
sqlConnection.Close();
}
Console.WriteLine("Calling WCF Service,Transfer data using DataTable");
return dataTable;
}
//实现接口定义的方法,DataSet传递数据
public System.Data.DataSet GetDataByDataSet()
{
//这里可以定义数据持久化操作,访问数据库等
System.Data.DataSet dataSet = new System.Data.DataSet();
SqlConnection sqlConnection = new SqlConnection("Data Source=.\\SQL
EXPRESS;AttachDbFilename=|DataDirectory|\\Database\\DatabaseWCF.mdf;Integrated Security=True;User Instance=True");
try
{
System.Data.SqlClient.SqlDataAdapter sqlDataAdapter = new System.
Data.SqlClient.SqlDataAdapter("SELECT id, name, english_name FROM TableWCF",
sqlConnection);
sqlDataAdapter.Fill(dataSet, "TableWCF");
}
catch (Exception e)
{
}
finally
{
sqlConnection.Close();
}
Console.WriteLine("Calling WCF Service,Transfer data using dataSet");
return dataSet;
}
//实现接口定义的方法,Collection传递数据
public List<User> GetDataByCollection()
{
//这里可以定义数据持久化操作,访问数据库等
List<User> list = new List<User>();
for (int i = 0; i < 10; i++)
{
User user = new User();
user.age = 20+i;
user.name = "Frank Xu Lei:" + i.ToString();
}
Console.WriteLine("Calling WCF Service,Transfer data using Collection");
return list;
}
}
//3数据契约
[DataContract]
public class User
{
[DataMember]
public string name;
[DataMember]
public int age;
}
}
【3.2】托管宿主:
托管宿主的配置过程与前几节宿主类似,这里不在详述,配置文件里契约和MEX原数据节点一定要配置,具体代码如下:
<service behaviorConfiguration="WCFService.WCFServiceBehavior" name="WCF
Service.WCFService">
<endpoint
address="http://localhost:9003/WCFService"
binding="wsHttpBinding"
contract="WCFService.IWCFService">
endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataEx
change" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:9003/"/>
baseAddresses>
host>
service>
services>
<behaviors>
<serviceBehaviors>
<behavior name="WCFService.WCFServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
behavior>
serviceBehaviors>
behaviors>
【3.4】客户端:
宿主配置完毕,编译运行宿主程序,我们在客户端添加服务的引用,输入正确的元数据交换地址,查询服务,可以看到如下的操作查询结果,如图:
我们可以看到客户端反序列化的本地类的信息。DataTable和DataSet使用的依然是.NET 类库的类型。对应的代理服务操作如下:
return base.Channel.GetDataByTable();
}
public System.Data.DataSet GetDataByDataSet() {
return base.Channel.GetDataByDataSet();
}
但是我们定义的集合操作在反序列化为客户端操作以后以及发生了变化,客户单使用数组代替了我们的集合List.代码如下:
return base.Channel.GetDataByCollection();
}
WCF为集合类型提供了专属的封送机制,客户端发序列化的本地操作使用与之对应的数组。
【4】运行结果:
这里客户端使用了WinForm界面,借助DataGridView控件来显示数据,方便DEMO。分别绑定事件方法,通过客户端服务代理,调用WCF操作服务,获取数据。代码如下:
private void buttonDataTable_Click(object sender, EventArgs e)
{
WCFServiceClient wcfServiceProxy =
new WCFServiceClient("WSHttpBinding_IWCFService");
//调用服务,获取数据表dataTable,
System.Data.DataTable dataTable = wcfServiceProxy.GetDataByTable();
if (dataTable != null)
{
dataGridViewWCFDataTable.DataSource = dataTable;//绑定数据源到控件
}
}
//Get data using DataSet By WCF proxy
private void buttonDataSet_Click(object sender, EventArgs e)
{
WCFServiceClient wcfServiceProxy =
new WCFServiceClient("WSHttpBinding_IWCFService");
//调用服务,获取数据集dataSet,
System.Data.DataSet dataSet = wcfServiceProxy.GetDataByDataSet();
if (dataSet != null && dataSet.Tables.Count > 0)
{
dataGridViewWCFDataSet.DataSource = dataSet.Tables[0];//绑定数据源到控件
}
}
点击按钮,分别测试调用服务操作返回数据是否成功,运行结果如图:
【5】总结:
我们来看看使用这些类型进行数据传递的优点:
(1)在WCF中,还可以使用DataTable和DataSet的类型或者继承之数据集或者数据表。 对于WCF的客户端与服务而言,可以通过开发工具Visual Studio工具使用DataSet、DataTable以及它们的类型安全的派生对象进行数据的传输。
(2)在服务契约中使用数据表或者数据集还存在一个缺陷,那就是它可能暴露内部数据库表的数据结构。
(3)WCF为集合类型提供了专属的封送机制,客户端发序列化的本地操作使用与之对应的数组。.NET为集合类封装了丰富特性和操作,这也是我们使用的主要原因。
它们同样也有缺点,这个是我们必须注意的:
(1)如果全部是基于.net平台进行数据交换,比较方便,但是异构平台来说,这种方式过于繁琐。而且,这些数据访问类型都是特定的.NET类型。在序列化时,它们生成的数据契约样式也过于复杂,很难与其它平台进行交互;
(2)WCF主要的目标是面向服务,平台无关。但客户端必须知道ADO.NET关于此类的定义信息,,这些显然违背了面向服务的编程原则。
(3)使用序列化机制而不是WCF面向服务的数据契约特性,将来对数据库样式的修改会影响到客户端。虽然在应用程序内部可以传递数据表,但如果是跨越应用程序或公有的服务边界发送数据表。使用数组返回数据,代替DataTable和DataSet。