WCF分布式开发步步为赢(8):使用数据集(DataSet)、数据表(DataTable)、集合(Collection)传递数据
数据集(DataSet)、数据表(DataTable)、集合(Collection)概念是.NET FrameWork里提供数据类型,在应用程序编程过程中会经常使用其来作为数据的载体,属于ADO.NET的一部分。今天我们WCF分布式开发步步为赢第8节的内容:使用数据集(DataSet)、数据表(DataTable)、集合(Collection)传递数据。本节内容除了介绍几个类型概念外的,同样会详细给出代码的实现过程。此外我们会分析这几种数据类型的优势和缺点,以及在面向对象的服务开发过程中如何解决这些问题。
(昨天博客园发布文章出错,没办法只有现在重新发了,可惜我花了很久排版,没保存成功,结果还是要重新组织。大家有好的方法可以介绍一下~)
【1】数据集(DataSet)、数据表(DataTable):
我们首先来介绍这两个类型的相关概念,然后在介绍其在WCF应用程序开发中的使用方式。
【1.1】基本概念:
数据集(DataSet)、数据表(DataTable),相信大家都不回陌生,只要做过ADO.NET进行数据库编程的开发人员来说,都会使用到这两个类。DataSet 是 ADO.NET 结构的主要组件,它是从数据源中检索到的数据在内存中的缓存。DataSet 由一组 DataTable 对象组成,您可使这些对象与 DataRelation 对象互相关联。您还可通过使用 UniqueConstraint 和 ForeignKeyConstraint 对象在 DataSet 中实施数据完整性。有关使用 DataSet 对象的详细信息,请参见 在 ADO.NET 中使用 DataSet。
尽管 DataTable 对象中包含数据,但是 DataRelationCollection 允许您遍览表的层次结构。这些表包含在通过 Tables 属性访问的 DataTableCollection 中。当访问 DataTable 对象时,请注意它们是按条件区分大小写的。例如,如果一个 DataTable 被命名为“mydatatable”,另一个被命名为“Mydatatable”,则用于搜索其中一个表的字符串被认为是区分大小写的。但是,如果“mydatatable”存在而“Mydatatable”不存在,则认为该搜索字符串不区分大小写。有关使用 DataTable 对象的更多信息,请参见 创建 DataTable。
DataSet 可将数据和架构作为 XML 文档进行读写。数据和架构可通过 HTTP 传输,并在支持 XML 的任何平台上被任何应用程序使用。可使用 WriteXmlSchema 方法将架构保存为 XML 架构,并且可以使用 WriteXml 方法保存架构和数据。若要读取既包含架构也包含数据的 XML 文档,请使用 ReadXml 方法。
在典型的多层实现中,用于创建和刷新 DataSet 并依次更新原始数据的步骤包括:
- 通过 DataAdapter 使用数据源中的数据生成和填充 DataSet 中的每个 DataTable。
- 通过添加、更新或删除 DataRow 对象更改单个 DataTable 对象中的数据。
- 调用 GetChanges 方法以创建只反映对数据进行的更改的第二个 DataSet。
- 调用 DataAdapter 的 Update 方法,并将第二个 DataSet 作为参数传递。
- 调用 Merge 方法将第二个 DataSet 中的更改合并到第一个中。
- 针对 DataSet 调用 AcceptChanges。或者,调用 RejectChanges 以取消更改。
【1.2】
DataSet 和 DataTable 对象从 MarshalByValueComponent 继承而来,并支持用于远程处理的 ISerializable 接口。这些是仅有的可以远程处理的 ADO.NET 对象。 我们先来看一下DataSet的定义,使用Reflector工具查看,部分代码如下:
[Serializable, ToolboxItem("Microsoft.VSDesigner.Data.VS.DataSetToolboxItem, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), DefaultProperty("DataSetName"), XmlSchemaProvider("GetDataSetSchema"), ResDescription("DataSetDescr"), XmlRoot("DataSet"), Designer("Microsoft.VSDesigner.Data.VS.DataSetDesigner, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
public class DataSet : MarshalByValueComponent, IListSource, IXmlSerializable, ISupportInitializeNotification, ISupportInitialize, ISerializable
{
// Fields
private bool _caseSensitive;
private CultureInfo _culture;
private bool _cultureUserSet;
private string _datasetPrefix;
private object _defaultViewManagerLock;
private readonly int _objectID;
private static int _objectTypeCount;
private SerializationFormat _remotingFormat;
private string dataSetName;
private DataViewManager defaultViewManager;
private bool enforceConstraints;
internal PropertyCollection extendedProperties;
private bool fBoundToDocument;
internal bool fEnableCascading;
internal bool fInitInProgress;
}
DataTable的部分实现代码如下:
[Serializable, Editor("Microsoft.VSDesigner.Data.Design.DataTableEditor, Microsoft.VSDesigner, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), DefaultProperty("TableName"), DesignTimeVisible(false), ToolboxItem(false), XmlSchemaProvider("GetDataTableSchema"), DefaultEvent("RowChanging")]
public class DataTable : MarshalByValueComponent, IListSource, ISupportInitializeNotification, ISupportInitialize, ISerializable, IXmlSerializable
{
// Fields
private bool _caseSensitive;
private bool _caseSensitiveUserSet;
internal DataColumn _colUnique;
private CompareOptions _compareFlags;
private CompareInfo _compareInfo;
}
在WCF分布式应用程序开发过程中,可以使用来作为数据契约类型,在服务和客户端传递。两者在定义之初都包含了[Serializable]声明,因此两者的对象都是可以序列化的。可以使用在客户端和服务端传递数据。
【
【2】集合(Collection):
集合也是我们编程开发中经常使用的类型。
【2.1】基本概念:
.NET Framework 提供了用于数据存储和检索的专用类。这些类提供对堆栈、队列、列表和哈希表的支持。大多数集合类实现相同的接口,可继承这些接口来创建适应更为专业的数据存储需要的新集合类。针对 .NET Framework 的 2.0 版和更高版本的应用程序应当使用 System.Collections.Generic 命名空间中的泛型集合类,与对应的非泛型类相比,这些类提供了更高的类型安全性和效率。
集合类具有以下特点:
集合类定义为 System.Collections 或 System.Collections.Generic 命名空间的一部分。大多数集合类都派生自 ICollection、IComparer、IEnumerable、IList、IDictionary 和 IDictionaryEnumerator 接口以及它们的等效泛型接口。使用泛型集合类可以提供更高的类型安全性,在某些情况下还可以提供更好的性能,尤其是在存储值类型时,这些优势会体现得更明显。有关更多信息,请参见泛型的优点。
如果将紧密相关的数据组合到一个集合中,则能够更有效地处理这些紧密相关的数据。代替编写不同的代码来处理每一单独的对象,您可以使用相同的调用代码来处理一个集合的所有元素。
若要管理集合,可使用 Array 类和 System.Collections 类添加、移除和修改该集合中的个别元素或某一范围内的元素。甚至可以将整个集合复制到另一个集合中。某些 Collections 类具有排序功能并且大多数都有索引。自动处理内存管理,集合的容量会根据需要扩展。当访问集合成员时同步提供线程安全。某些 Collections 类可以生成包装,这些包装令集合是只读的或固定大小的。任何 Collections 类都可以生成自己的枚举数,该枚举数简化了对元素的循环访问。
在 .NET Framework 2.0 版中,泛型集合类提供了新功能,并且使得创建强类型集合变得容易。请参见 System.Collections.Generic 和 System.Collections.ObjectModel 命名空间。
【2.2】集合数据契约:
集合有如此强大的特性,这也是我们使用的一个重要原因。
【3】示例代码分析:
下面我们来介绍一下使用Dataset、 Datatable和集合类来传递数据的程序开发过程。依次介绍服务契约、宿主、客户端的开发配置过程,另外服务端设计了一个数据库,添加了部分演示数据,目的是方便Demo。
【3.1】服务契约:
服务契约定义了3个操作契约,分别是使用Dataset、Datatable、List来传递数据,WCF服务类实现了接口定义的操作契约,分别返回不同的数据结构类型。具体代码如下:
//ServiceContract 属性以及 Indigo 使用的所有其他属性均在 System.ServiceModel 命名空间中定义,
//因此本例开头使用 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=.//SQLEXPRESS;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=.//SQLEXPRESS;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原数据节点一定要配置,具体代码如下:
<services>
<service behaviorConfiguration="WCFService.WCFServiceBehavior" name="WCFService.WCFService">
<endpoint
address="http://localhost:9003/WCFService"
binding="wsHttpBinding"
contract="WCFService.IWCFService">
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<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 类库的类型。对应的代理服务操作如下:
public System.Data.DataTable GetDataByTable() {
return base.Channel.GetDataByTable();
}
public System.Data.DataSet GetDataByDataSet() {
return base.Channel.GetDataByDataSet();
}
但是我们定义的集合操作在反序列化为客户端操作以后以及发生了变化,客户单使用数组代替了我们的集合List.代码如下:
public User[] GetDataByCollection() {
return base.Channel.GetDataByCollection();
}
WCF为集合类型提供了专属的封送机制,客户端发序列化的本地操作使用与之对应的数组。
【4】运行结果:
这里客户端使用了WinForm界面,借助DataGridView控件来显示数据,方便DEMO。分别绑定事件方法,通过客户端服务代理,调用WCF操作服务,获取数据。代码如下:
//Get data using DataTable By WCF proxy
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。
以上就是本节的全部内容,下面上传本节的示例代码供大家参考,/Files/frank_xl/WCFServiceDataSetFrankXuLei.rar。(大家有好的博客写作的经验,请赐教~我出现好几次写的文章保存失败,丢失的情况了。live writer怎么样?)
参考资料:
1.《DataSet》,http://baike.baidu.com/view/624618.html;
2.《DataSet类》,http://msdn.microsoft.com/zh-cn/library/system.data.dataset(VS.80).aspx
3.《集合和数据结构》,http://msdn.microsoft.com/zh-cn/library/7y3x785f(VS.80).aspx
4.《Programming in WCF Service》
WCF分布式开发步步为赢(8):使用数据集(DataSet)、数据表(DataTable)、集合(Collection)传递数据的更多相关文章
- WCF分布式开发步步为赢(11):WCF流处理(Streaming)机制
WSE3.0框架提供了数据优化传输机制,WSE3.0构建Web服务安全(4):MTOM消息传输优化和文件上传.下载 疑问里进行了介绍.WCF同样也提供了流操作来支持大数据对象的传输和处理优化机制,今天 ...
- WCF分布式开发步步为赢(10):请求应答(Request-Reply)、单向操作(One-Way)、回调操作(Call Back).
WCF除了支持经典的请求应答(Request-Reply)模式外,还提供了什么操作调用模式,他们有什么不同以及我们如何在开发中使用这些操作调用模式.今天本节文章里会详细介绍.WCF分布式开发步步为赢( ...
- WCF分布式开发步步为赢(7):WCF数据契约与序列化
本节继续学习WCF分布式开发步步为赢(7):WCF数据契约与序列化.数据契约是WCF应用程序开发中一个重要的概念,毫无疑问实现客户端与服务端数据契约的传递中序列化是非常重要的步骤.那么序列化是什么?为 ...
- WCF分布式开发步步为赢(5)服务契约与操作重载
继上一节WCF分布式开发步步为赢系列的(4):WCF服务可靠性传输配置与编程开发,本节我们继续学习WCF分布式开发步步为赢的第(5)节:服务契约与操作重载.这里我们首先讲解OOP面向对象的编程中方法重 ...
- WCF分布式开发步步为赢(6):WCF服务契约继承与分解设计
上一节我们学习了WCF分布式开发步步为赢(5)服务契约与操作重载部分.今天我们来继续学习WCF服务契约继承和服务分解设计相关的知识点.WCF服务契约继承有何优势和缺点?实际项目里契约设计有什么原则和依 ...
- WCF分布式开发步步为赢(4):WCF服务可靠性传输配置与编程开发
今天继续WCF分布式开发步步为赢系列的第4节:WCF服务可靠性传输配置与编程开发.这个章节,我们要介绍什么是WCF服务的可靠性传输,随便介绍网络协议的概念,Web Service为什么不支持可靠性传出 ...
- WCF分布式开发步步为赢(3)WCF服务元数据交换、配置及编程开发
今天我们继续WCF分布式开发步步为赢(3)WCF服务元数据交换.配置及编程开发的学习.经过前面两节的学习,我们了解WCF分布式开发的相关的基本的概念和自定义宿主托管服务的完整的开发和配置过程.今天我们 ...
- WCF分布式开发步步为赢(1):WCF分布式框架基础概念
众所周知,系统间的低耦合一直是大型企业应用系统集成追寻的目标,SOA面向服务架构的出现为我们的如何利用现有企业系统资源进行企业ERP系统设计和实现提供了重要的参考原则.SOA如此炙手可热,各大厂商都推 ...
- WCF分布式开发步步为赢(15):错误契约(FaultContract)与异常处理(ExceptionHandle)
今天学习WCF分布式开发步步为赢系列的15节:错误契约(FaultContract)与异常处理(ExceptionHandle).本节内容作为WCF分布式开发的一个重要知识点,无论在学习还是项目中都应 ...
随机推荐
- spring 中的两个DaoSupport类的使用对比
可以利用NamedParameterJdbcDaoSupport 已经封装的NamedParameterJdbcTemplate方便的进行sql中参数的初始化工作. 相对于JdbcDaoSupport ...
- Word2013死机的问题
Word2013用公式编辑器经常死机,网上介绍方法.试试看https://zhidao.baidu.com/question/937023473910649252.html1.启动word2013 2 ...
- Go语言工程结构
Go是一门推崇软件工程理念的编程语言. Go的代码必须放在GOPATH目录下,它应该包含三个子目录: src:用于以代码包的形式组织并保存Go源码文件.应该分为三类:库源码文件.命令源码文件.测试源码 ...
- [转]network-manager与interfaces冲突
[转]network-manager与interfaces冲突 http://blog.sina.com.cn/s/blog_48a45b9501010681.html 网络配置的两种方式 ...
- 运算符 swift
1nil聚合运算符 nil coalescing operator a ?? b ==>a!=nil ? a! : b 要求: 1a是一个可选类型 2b必须和a解包后类型一致 var userN ...
- InputStream和OutputStream与String之间的转换
//1.字符串转inputstream String str="aaaaa"; InputStream in = new ByteArrayInputStream(str.getB ...
- 浅谈ERP系统实施后如何完善企业内部控制制度建设
ERP与企业内部控制制度,前者提升企业的管理水平,后者为企业发展保驾护航,两项工作都是企业各项工作的重中之重. ERP是企业资源规划Enterprise Resource Planning的缩写.企业 ...
- JS中的forEach、$.each、map方法
forEach是ECMA5中Array新方法中最基本的一个,就是遍历,循环.例如下面这个例子: [1, 2 ,3, 4].forEach(alert); 等同于下面这个for循环 var array ...
- MyEclipse运行很慢的原因
myEclipse以其丰富的功能博得程序员的热爱,但是其速度确实有问题,jsp文 件打开会不停的校验,甚至出现卡死,分析原因,原来是 validation在做怪. 好,既然找到了原因,那就把问题解决, ...
- android 通过AlarmManager实现守护进程
场景:在app崩溃或手动退出或静默安装后能够自动重启应用activity 前提:得到系统签名 platform.pk8.platform.x509.pem及signapk.jar 三个文件缺一不可(系 ...