一、概述

前面2篇文章,介绍了使用SqlCommand对象利用sql命令来操作数据库。

这篇文章我们来介绍使用c#的DataSet 和 DataAdaper对象操作操作数据库。

先来介绍下这两个对象是干啥的。

1、DataSet对象

顾名思义,DataSet 可叫做数据集,可以简单的理解为一个临时数据库,其中可包含多个表(DataTable),表中有记录。

DataSet将从数据源(如数据库)中获得的数据保存在内存中,应用程序与内存中的DataSet进行交互,在这期间,不需要连接数据库。

也就是说,一旦DataSet获取数据后,就可以把数据库链接关闭掉,这可以节约资源,提高数据的访问和处理速度(因为是在内存中操作)。

当然,也是因为DataSet是在内存中,可以理解成一个缓存,数据放的越多,占系统内存越多,这个也是需要考虑的。

应用程序除了可以从DataSet中获取数据,还可以更新DataSet中的数据,后面可以使用DataAdaper对象同步到数据源中,避免了额外的写代码处理,提高了代码编写的效率和质量。

2、DataAdaper对象

DataAdaper,可称为数据适配器,它就是负责把DataSet和真实的数据源连接起来。本身DataSet对象是不直接和数据源打交道的,它只是个缓存,用户存储数据。

DataAdaper的工作机制是,它利用Connection对象连接数据库,使用相关的Command对象封装的命令(如sql语句)获取数据,并把得到的数据填充到DataSet对象中。后续DataSet对象中的数据更新后需要同步到数据源中,也是由DataAdaper对象使用相关的Command对象来负责同步数据。

下面我们通过例子来说明如何使用。

操作的表 userinfo有两个字段 username 和 password,其中username为主键。

二、查询数据举例

直接上例子代码:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Windows.Forms; namespace DbExample
{
class DataSetExample
{
public void fetchData()
{
DataSet dataSet = new DataSet();
SqlDataAdapter adapter = new SqlDataAdapter();
SqlConnection conn = getConnection();
try
{
conn.Open();
SqlCommand command = new SqlCommand("select * from userinfo", conn); //绑定的sql命令
adapter.SelectCommand = command;
adapter.Fill(dataSet, "userinfo"); //将数据获取后装载到dataSet中,当作一张表存储在内存中
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "查询数据失败");
}
finally
{
conn.Close();
}
showTable(dataSet);
} private void showTable(DataSet dataSet)
{
DataTable table = dataSet.Tables["userinfo"]; //也可以通过序号获取
int num = table.Rows.Count;
MessageBox.Show(num.ToString()); //获取表的记录数
for (int i = ; i < num; i++)
{
string re = table.Rows[i][].ToString() + "," + table.Rows[i][].ToString();
MessageBox.Show(re);
}
// 或者下面的方式遍历更好
foreach (DataRow row in table.Rows)
{
string re = row[].ToString() + "," + row[].ToString();
MessageBox.Show(re);
}
//或者
DataRow[] rows = table.Select(); //可以通过参数控制输出的内容,还可以排序
foreach (DataRow row in table.Rows)
{
string re = row[].ToString() + "," + row[].ToString();
MessageBox.Show(re);
}
} private SqlConnection getConnection()
{
string strConnection = @"Data Source = localhost\SQLEXPRESS; Initial Catalog = mydb; User Id = sa; Password = 12345678;";
SqlConnection conn = new SqlConnection(strConnection);
return conn;
}
}
}

从上面代码中可以看出, showTable方法是在conn关闭后执行的,说明了一旦数据到了dataset中,就在内存中了,与数据库是否保持连接无关了。
也可以看出,DataAdaper对象是利用绑定的command对象来实际获取数据的,并利用自己的Fill方法将数据放到dataset对象中。

三、同步更新数据举例(自动生成更新命令)

上面我们举了个查询的数据,没有对dataset数据进行修改。下面例子会对dataset数据进行修改,然后同步到数据库中。

我们先给出一个例子代码:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Windows.Forms; namespace DbExample
{
class DataSetExample
{
public void fetchData()
{
DataSet dataSet = new DataSet();
SqlDataAdapter adapter = new SqlDataAdapter();
SqlConnection conn = getConnection();
try
{
conn.Open();
SqlCommand command = new SqlCommand("select * from userinfo", conn); //绑定的sql命令
adapter.SelectCommand = command;
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
adapter.Fill(dataSet, "userinfo"); //将数据获取后装载到dataSet中,当作一张表存储在内存中
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "操作失败");
}
finally
{
conn.Close();
}
deleteRow(dataSet, adapter);
MessageBox.Show("操作成功");
} private void deleteRow(DataSet dataSet, SqlDataAdapter adapter)
{
DataTable table = dataSet.Tables["userinfo"];
table.Rows[].Delete(); //删除第2条
adapter.Update(dataSet, "userinfo"); //同步到数据库
} private SqlConnection getConnection()
{
string strConnection = @"Data Source = localhost\SQLEXPRESS; Initial Catalog = mydb; User Id = sa; Password = Xqh980234;";
SqlConnection conn = new SqlConnection(strConnection);
return conn;
}
}
}

与前面第一个查询数据的例子相比,我们在fetchData方法的try代码块中增加了一行代码
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);

在fetchData方法的结尾将调用showTable 方法 改为 调用deleteRow方法。

执行上面代码,我们会发现userinfo表中的第3条记录被删除了,但是代码中没看到任何显示的 delete sql语句的执行。

下面我们来解释下背后的原因。

在文章的开头我们提到,DataSet对象只是个内存缓存,它是通过DataAapter对象来操作数据库的,而DataAapter对象是通过其关联的Command对象来具体实现数据库操作的。

我们看到的代码中的如下语句:

SqlCommand command = new SqlCommand("select * from userinfo", conn); //绑定的sql命令
adapter.SelectCommand = command;

这个DataAapter对象绑定了 SelectCommand 对象,这个Command通过其指定的查询sql获取数据后放到DataSet对象中。

对于查询命令,还有一种更简单的方式,用一个语句将上面两条语句合一,如下:

adapter = new SqlDataAdapter("select * from userinfo", conn);

除了查询外,还有DeleteCommand、InsertCommand、UpdateCommand 来完成更新操作。

但是在特定条件下,这三个Command不需要显示的指定,只需要加上下面语句。

SqlCommandBuilder builder = new SqlCommandBuilder(adapter);

加上这个语句后,就会自动构建三个Command对象。

这就是为什么上面的代码没看到有DeleteCommand对象,却可以通过DataAapter来实现数据库记录的删除操作。

需要说明的是,自动生成更新Command对象,需要满足的条件是:

必须要绑定SelectCommand 对象,且指定的select语句只能包含单张表的操作,还必须返回至少一个主键或唯一列。

还需要注意的是:

要想同步数据库,完成记录的删除,不能调用 table.Rows.Remove 或 Clear方法 ,这些方法会删除DataSet中的数据,但DataAapter不是以这个为依据来判断是否要删除记录,而是要调用DataRow对象的Delete方法,如上面的代码:

table.Rows[2].Delete();

需要说明的是,调用了上面的Delete方法,在同步到数据库之前,这条记录在DataSet中还存在,但是却不能访问它,访问会报错。

但是在同步之后,该记录将会从DataSet中被删除。

而且要注意的是,在同步前,不能调用rows的Remove方法把该记录从DataSet中删除,这样的话,同步时,记录将不会从数据中删除。

上面讲的几个注意点比较绕,需要弄清楚,并且写代码时要注意,否则很容易会导致数据紊乱,与预期的不一致。

上面我们看了删除记录的操作,下面我们看下增加记录。

增加记录比较简单,没有特别的注意点,代码如下:

DataTable table = dataSet.Tables["userinfo"];
table.Rows.Add(new string[] { "x1","p1"});
table.Rows.Add(new string[] { "x2", "p2" });
adapter.Update(dataSet, "userinfo"); //同步到数据库

上面代码先往dataSet中插入2条记录,再同步到数据库。注意,无论是同步前,还是同步后,都能从dataSet中访问到这两条记录。

我们再看如何修改记录同步到数据库。

DataTable table = dataSet.Tables["userinfo"];
table.Rows[2][0] = "x21";
table.Rows[2][1] = "kkk";
adapter.Update(dataSet, "userinfo"); //同步到数据库

上面代码修改dataSet中的第3条记录的两个字段,再同步到数据库。注意,无论是同步前,还是同步后,都能从dataSet中访问到这两条记录。

四、同步更新数据(非自动)

上面介绍了如何将修改后的DataSet数据同步到数据库中,并且更新命令是自动生成的,下面介绍如何手工设置。

我们先看一个update的例子。

public void fetchDataEx()
{
DataSet dataSet = new DataSet();
SqlDataAdapter adapter = null;
SqlConnection conn = getConnection();
try
{
conn.Open();
adapter = new SqlDataAdapter("select username,password from userinfo", conn);
adapter.UpdateCommand = new SqlCommand(
"update userinfo set password=@password " +
"where username =@username", conn);
adapter.UpdateCommand.Parameters.Add("@password", SqlDbType.VarChar, , "password");
adapter.UpdateCommand.Parameters.Add("@username", SqlDbType.VarChar, , "username");
adapter.Fill(dataSet, "userinfo");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "操作失败");
}
finally
{
conn.Close();
}
deleteRow(dataSet, adapter);
MessageBox.Show("操作成功");
} private void deleteRow(DataSet dataSet, SqlDataAdapter adapter)
{
DataTable table = dataSet.Tables["userinfo"];
table.Rows[][] = "y26";
adapter.Update(dataSet, "userinfo"); }

上面代码,adapter显示的绑定了一个UpdateCommand对象,其中包含了一个update语句。
需要注意的是,上面的update语句没有更新主键username,如果要更新主键,则需要采用如下的写法。

adapter.UpdateCommand = new SqlCommand(
"update userinfo set username =@newusername,password=@password " +
"where username =@username", conn);
adapter.UpdateCommand.Parameters.Add("@password", SqlDbType.VarChar, , "password");
adapter.UpdateCommand.Parameters.Add("@newusername", SqlDbType.VarChar, , "username");
SqlParameter parameter = adapter.UpdateCommand.Parameters.Add("@username", SqlDbType.VarChar, , "username");
parameter.SourceVersion = DataRowVersion.Original; //更新DataSet的代码
DataTable table = dataSet.Tables["userinfo"];
table.Rows[][] = "good2";
table.Rows[][] = "xxx12";
adapter.Update(dataSet, "userinfo"); //同步到数据库

上面代码可以看出,利用 parameter.SourceVersion = DataRowVersion.Original 将update语句中的where条件的username字段的取值设置为取原始值。

插入和删除代码的命令设置如下

adapter.DeleteCommand = new SqlCommand( "delete from userinfo where username =@username", conn);
adapter.DeleteCommand.Parameters.Add("@username", SqlDbType.VarChar, , "username"); adapter.InsertCommand = new SqlCommand("insert into userinfo values(@username,@password)", conn);
adapter.InsertCommand.Parameters.Add("@username", SqlDbType.VarChar, , "username");
adapter.InsertCommand.Parameters.Add("@password", SqlDbType.VarChar, , "password");

可以看出,如果是单表操作,最好还是采用上个例子讲的自动生成更新command的方式,人工设置的方式还是比较麻烦的。

还有需要注意的时候,有时对DataSet做了多次操作,会导致对更新数据库的顺序(也就是说对执行 update ,delete,insert的顺序)有要求。

这时需要显示的指明同步的顺序。这时可以使用 DataTable 的 Select 方法来返回仅引用具有特定 RowState 的 DataRow 数组。然后可以将返回的 DataRow 数组传递到 DataAdapter 的 Update 方法来处理已修改的行。通过指定要更新的行的子集,可以控制处理插入、更新和删除的顺序。如:

DataTable table = dataSet.Tables["userinfo"];
// First process deletes.
adapter.Update(table.Select(null, null, DataViewRowState.Deleted));
// Next process updates.
adapter.Update(table.Select(null, null, DataViewRowState.ModifiedCurrent));
// Finally, process inserts.
adapter.Update(table.Select(null, null, DataViewRowState.Added));

本文介绍了,如何利用DataSet 和 DataAdaper对象来操作数据库。这里介绍的是单表操作。对于跨表的操作,会更为复杂,这在后面的文章中介绍。

c# 数据库编程(利用DataSet 和 DataAdaper对象操作数据库--单表操作)的更多相关文章

  1. c# 数据库编程(利用DataSet 和 DataAdaper对象操作数据库--跨表操作)

    上篇文章我们介绍了如何利用DataSet 和 DataAdaper对象来对单张表进行操作. 本文我们将介绍如何进行跨表操作. 我们通过具体例子方式进行演示,例子涉及到三张表. 1)student表(学 ...

  2. day 46 Django 学习3 数据库单表操作以及反向解析

    前情提要: Django 已经学了不少了, 今天学习链接数据库的操作.以及相关的反向解析等 一:反向解析 1:反向解析模板层 跳转时设定url会随着前面的路由改变而改变         2:反向解析之 ...

  3. django系列5.2--ORM数据库的单表操作

    单表操作 在views.py中添加对数据库的操作语句 #在逻辑代码中导入你要操作的表 from app import models def add_book(request): ''' 添加表记录 : ...

  4. 模型层之ORM、数据库和单表操作

    一.ORM简介 ORM是“对象-关系-映射”的简称,一般指持久化数据和实体对象的映射 1.1 什么是“持久化” 持久(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中( ...

  5. 运用Python语言编写获取Linux基本系统信息(三):Python与数据库编程,把获取的信息存入数据库

    运用Python语言编写获取Linux基本系统信息(三):Python与数据库编程 有关前两篇的链接: 运用Python语言编写获取Linux基本系统信息(一):获得Linux版本.内核.当前时间 运 ...

  6. 《天书夜读:从汇编语言到windows内核编程》八 文件操作与注册表操作

    1)Windows运用程序的文件与注册表操作进入R0层之后,都有对应的内核函数实现.在windows内核中,无论打开的是文件.注册表或者设备,都需要使用InitializeObjectAttribut ...

  7. Django学习笔记--数据库中的单表操作----增删改查

    1.Django数据库中的增删改查 1.添加表和字段 # 创建的表的名字为app的名称拼接类名 class User(models.Model): # id字段 自增 是主键 id = models. ...

  8. Hibernate学习笔记(一)-->数据库单表操作

    Hibernate框架是一个全ORM映射框架,是一个非常流行的数据库操作框架之一,现在比较流行的还有MyBatis半ORM映射框架 在MyEclipse IDE开发工具中,可以很轻松的搭建Hibern ...

  9. PHP单表操作mysqli数据库类的封装

    class DB{ private $options=array( 'database_type' => 'mysql', 'database_name' => 'test', 'serv ...

随机推荐

  1. c++builder 重载WindowProc、WndProc 截获消息(比Delphi多一个Message Map方法)

    c++builder 重载WindowProc.WndProc 截获消息 方法一WindowProc void __fastcall  myWindowProc(Messages::TMessage ...

  2. js获取中英文长度

    function getLength(str) {    var len = str.length;    var reLen = 0;    for (var i = 0; i < len; ...

  3. [置顶] SOLR 4.4 部署

    SOLR 4.4 部署 前言:近期研究下solr4.4的部署,一下是部署步骤,与大家分享下. 下载solr4.4.0.zip 地址        http://mirror.esocc.com/apa ...

  4. perl5 第五章 文件读写

    第五章 文件读写 by flamephoenix 一.打开.关闭文件二.读文件三.写文件四.判断文件状态五.命令行参数六.打开管道 一.打开.关闭文件   语法为open (filevar, file ...

  5. 宣布发布 Windows Azure 导入/导出服务的预览版以及 Web 和移动解决方案场景的若干增强功能

    客户评估基于云的存储解决方案时,面临的挑战之一是以经济高效.安全快速的方式从 Blob 存储区移进和移出大量数据.今天,我们很高兴地宣布发布 Windows Azure 导入/导出的预览版,这款新服务 ...

  6. 算法导论 6.5.9 堆实现K路归并问题

    问题: 设计一个时间复杂度为O(NlogK)的算法,它能够将K个有序链表合并为一个有序链表,这里的N为所有输入链表包含的总的元素个数 分析: 该问题为经典的利用堆完成K路归并的问题: 当K个序列满足一 ...

  7. wpf全局异常

    在App.xaml文件中 添加DispatcherUnhandledExceptionEventArgs 新增对应事件

  8. SharePoint迁移数据到生产环境

    SharePoint迁移数据到生产环境步骤如下: 1. 安装部署好生产环境 2. 配置管理中心 3. 安装SPD工具 4. 备份数据库(放在数据库服务器) 5. 备份wsp包(部署在管理中心服务器) ...

  9. ELF文件数据布局探索(1)

    作为一名Linux小白,第一次看到a.out这个名字,感觉实在是奇怪,搜了一下才知道这是编译器输出的默认可执行文件名 然后vi一下,哇,各种乱码,仔细看看,发现了三个清晰的字符ELF.继续搜索, 第一 ...

  10. 调用 sphinx-build生成HTML文件

    安装 Sphinx $ easy_install sphinx Searching for sphinx Reading http://pypi.python.org/simple/sphinx/ R ...