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 并依次更新原始数据的步骤包括:
  1. 通过 DataAdapter 使用数据源中的数据生成和填充 DataSet 中的每个 DataTable。
  2. 通过添加、更新或删除 DataRow 对象更改单个 DataTable 对象中的数据。
  3. 调用 GetChanges 方法以创建只反映对数据进行的更改的第二个 DataSet。
  4. 调用 DataAdapter 的 Update 方法,并将第二个 DataSet 作为参数传递。
  5. 调用 Merge 方法将第二个 DataSet 中的更改合并到第一个中。
  6. 针对 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 ]; // 绑定数据源到控件
            }
        }
     点击按钮,分别测试调用服务操作返回数据是否成功,运行结果如图:
  我们来看看使用这些类型进行数据传递的优点:
(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》



 本文转自 frankxulei 51CTO博客,原文链接:http://blog.51cto.com/frankxulei/320412,如需转载请自行联系原作者


相关文章
|
1月前
|
存储 缓存 算法
分布式缓存有哪些常用的数据分片算法?
【10月更文挑战第25天】在实际应用中,需要根据具体的业务需求、数据特征以及系统的可扩展性要求等因素综合考虑,选择合适的数据分片算法,以实现分布式缓存的高效运行和数据的合理分布。
|
2月前
|
JSON 分布式计算 前端开发
前端的全栈之路Meteor篇(七):轻量的NoSql分布式数据协议同步协议DDP深度剖析
本文深入探讨了DDP(Distributed Data Protocol)协议,这是一种在Meteor框架中广泛使用的发布/订阅协议,支持实时数据同步。文章详细介绍了DDP的主要特点、消息类型、协议流程及其在Meteor中的应用,包括实时数据同步、用户界面响应、分布式计算、多客户端协作和离线支持等。通过学习DDP,开发者可以构建响应迅速、适应性强的现代Web应用。
|
2月前
|
NoSQL Java Redis
开发实战:使用Redisson实现分布式延时消息,订单30分钟关闭的另外一种实现!
本文详细介绍了 Redisson 延迟队列(DelayedQueue)的实现原理,包括基本使用、内部数据结构、基本流程、发送和获取延时消息以及初始化延时队列等内容。文章通过代码示例和流程图,逐步解析了延迟消息的发送、接收及处理机制,帮助读者深入了解 Redisson 延迟队列的工作原理。
|
4月前
|
数据采集 分布式计算 并行计算
Dask与Pandas:无缝迁移至分布式数据框架
【8月更文第29天】Pandas 是 Python 社区中最受欢迎的数据分析库之一,它提供了高效且易于使用的数据结构,如 DataFrame 和 Series,以及大量的数据分析功能。然而,随着数据集规模的增大,单机上的 Pandas 开始显现出性能瓶颈。这时,Dask 就成为了一个很好的解决方案,它能够利用多核 CPU 和多台机器进行分布式计算,从而有效地处理大规模数据集。
230 1
|
4月前
|
运维 安全 Cloud Native
核心系统转型问题之分布式数据库和数据访问中间件协作如何解决
核心系统转型问题之分布式数据库和数据访问中间件协作如何解决
|
4月前
|
开发者 云计算 数据库
从桌面跃升至云端的华丽转身:深入解析如何运用WinForms与Azure的强大组合,解锁传统应用向现代化分布式系统演变的秘密,实现性能与安全性的双重飞跃——你不可不知的开发新模式
【8月更文挑战第31天】在数字化转型浪潮中,传统桌面应用面临新挑战。本文探讨如何融合Windows Forms(WinForms)与Microsoft Azure,助力应用向云端转型。通过Azure的虚拟机、容器及无服务器计算,可轻松解决性能瓶颈,满足全球用户需求。文中还提供了连接Azure数据库的示例代码,并介绍了集成Azure Storage和Functions的方法。尽管存在安全性、网络延迟及成本等问题,但合理设计架构可有效应对,帮助开发者构建高效可靠的现代应用。
33 0
|
4月前
|
Java 数据库连接 微服务
揭秘微服务架构下的数据魔方:Hibernate如何玩转分布式持久化,实现秒级响应的秘密武器?
【8月更文挑战第31天】微服务架构通过将系统拆分成独立服务,提升了可维护性和扩展性,但也带来了数据一致性和事务管理等挑战。Hibernate 作为强大的 ORM 工具,在微服务中发挥关键作用,通过二级缓存和分布式事务支持,简化了对象关系映射,并提供了有效的持久化策略。其二级缓存机制减少数据库访问,提升性能;支持 JTA 保证跨服务事务一致性;乐观锁机制解决并发数据冲突。合理配置 Hibernate 可助力构建高效稳定的分布式系统。
72 0
|
4月前
|
监控 中间件
分布式链路监控系统问题之当某个Segment数据缺失时还原调用树的问题如何解决
分布式链路监控系统问题之当某个Segment数据缺失时还原调用树的问题如何解决
|
前端开发
WCF更新服务引用报错的原因之一
WCF更新服务引用报错的原因之一
|
C# 数据安全/隐私保护
c#如何创建WCF服务到发布(SqlServer版已经验证)
c#如何创建WCF服务到发布(SqlServer版已经验证)
73 0