使用数据集(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 命名空间的一部分。大多数集合类都派生自 违规广告ection、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》

时间: 2024-10-12 10:24:48

使用数据集(DataSet)、数据表(DataTable)、集合(Collection)传递数据的相关文章

MySQL为数据表的指定字段插入数据

username not null 没有默认值/有默认值   insert不插入username字段 均不报错 2014年07月23日21:05    百科369 MySQL为数据表的指定字段插入数据 为数据表的指定字段插入数据,就是在INSERT语句中只向部分字段中插入值,而其它字段的值为数据表定义时的默认值. 基本的语法格式如下: INSERT INTO 表名(字段1,字段2,...,字段m) VALUES(值1,值2,...,值m); 字段1,字段2,...,字段m:表示数据表中的字段名称

数据库、数据表的基本操作及查询数据

数据库的基本操作 创建数据库 CREATE DATABASE database_name database_name为要创建的数据库的名称 删除数据库 DROP DATABASE database_name database_name为要删除的数据库的名称 数据库存储引擎 数据库存储引擎是数据库底层软件组成,数据库管理系统(DBMS)使用数据引擎进行创建.查询.更新和删除数据操作. MySQL的核心就是存储引擎. 存储引擎比较 |功能|MyISAM|Memory|InnoDB|Archive|

MySql 获取数据表中随机一条数据

通过sql语句获取数据表中的随意一条数据 -- rand() 获取0到1之间的随机浮点数 -- ((select max(id) from test) - (select min(id) from test)) * rand() 获取到随机数 -- + (select min(id) from test) 保证id为 最小ID与最大ID之间的数 select * from test where id >= ((select max(id) from test) - (select min(id)

Oracle 函数 “判断数据表中不存在的数据,才允许通过”

1 create or replace function mca_detail_material_val(p_material_code VARCHAR2, --实参 2 p_material_name VARCHAR2, 3 p_mca_no VARCHAR2 4 ) 5 RETURN varchar2 AS 6 7 --物料编码/名称的验证,数据表(MATERIAL)中 不存在 该物料编码/名称,允许通过,填其他值都不允许通过 8 9 v_count1 NUMBER; -- 形参 10 v_

设置mysql数据表列自动递增以及数据行插入操作

创建mysql数据表,设置id列递增.主键create table running_log ( id int primary key auto_increment, routename varchar(255), log varchar(255), time datetime ); 往有递增数据列的数据表插入新的数据行 1.INSERT INTO `running_log` (`id`, `routename`, `log`, `time`) VALUES ('null', 'yunnan-10

oracle——数据表的相关操作——删除数据表

创建数据表; create table 表名 ( 列明1 数据类型1 [约束性条件], 列明1 数据类型1 [约束性条件], …… ) tablespace 表空间 create table student05 ( student_id number not null, student_name varchar2(20), student_age number, status varchar2(2), version number default 0 ) tablespace test sele

如何在升级数据表的同时保留原数据

一.问题 项目做到后期,或者项目版本升级时,有时不得不更新原来的数据库设计.比如有一张表,可能需要增加字段.修改字段等.在某些情况下,Alter TABLE命令不能完成这个任务,比如更换主键.这时只能用全新的设计替换掉原来的设计.但是,如果让新设计的表格中,包含原来的数据呢? 二.解决方案 其实很简单,分4步: 创建新表 将原表数据插入到新表中 删除原表 将新表重命名为原表名 三.实例 知名开源项目BlogEngine.NET就是这样做的!比如以下是它从2.0版本升级到2.5版本时,所使用的SQ

oracle——数据表的相关操作——插入数据以及批量插入数据

SQL更新数据 1.插入数据——insert操作 语法格式如下: insert into 表名 (列名1,列名2……,列名n) values (值1,值2……,值n); 在insert操作中,列名列表中的各列需要以逗号分隔:而值列表指定各列的值,列名与值需要一一对应. 如果insert语句所指定的列名列表包含了表中的所有列,那么可以将列名列表省略. create table student08 ( id number, name varchar2(20), sex varchar2(20), a

从数据表中筛选重复的数据

If you want to find duplicate data (by one or several criterias) and select the actualrows. This should also work, maybe give it try. SELECT id, COUNT(id) FROM table1 GROUP BY id HAVING COUNT(id)>1; I think this will work properly to search repeated