《Windows CE嵌入式开发入门——基于Xscale架构》 第11章 Windows CE数据库程序开发

 

在移动设备上有几种数据库可用,但是本书所用的都是SQL Server 2000 Windows CE Edition,是微软公司桌面数据库的精简版(本书以后简称为SQL Server CE)。这是为.NET Compact Framework选择的移动数据库,因为它完全支持.NET Compact Framework运行时环境和.NET Compact Framework开发环境。

大多数SQL Server CE程序有两个主要的数据库任务。

n     当断开时操作数据;

n     当设备连到桌面计算机时传送数据到SQL Server

第一个数据库任务包含使用一般的ADO.NET类,这在前面已经讨论过了,至于提供者类,以后将讨论。在桌面系统上对于使用ADO.NET的程序员来说,.NET Compact Framework实现的ADO.NET的一般类都是很熟悉的。

第二个主要的数据库任务,SQL ServerSQL Server CE间的数据传输,不仅包括多个数据库引擎,也包括Microsoft Internet Information ServicesIIS)。这是两种不同的机制,对合并复制和远程数据访问(RDA)可能有所帮助。

11-1  显示在文件管
理器中的数据库文件

 

ADO.NET只能在托管代码中可用。但是在SQL Server CE数据库中,不需要使用托管代码访问数据。有两组单独的操作SQL Server CE数据库数据的API,一个是COM对象,另一个是OLE DB CE。这些组件与它们的桌面版本相匹配,即ADOOLE DB

11.1  SQL Server CE文件

每个SQL Server数据库以单个文件储存在Windows CE设备上。推荐的文件扩展名为.sdf,文件名称为数据库的名称。如打开一个名为mydb.sdf的数据库连接,就要用全称mydb.sdf,不能用不带sdf后缀的mydb。因为每个数据库是一个文件,所以在文件管理器中可见。图11-1显示的是在文件管理器选择的一个数据库文件。

因为SQL Server CE数据库是Windows CE文件系统中的文件,所以它们能使用FileDirectory类访问。例如,复制一个数据库文件、删除一个数据库文件或者测试一个指定名称的数据库文件是否存在。同样可以像其他文件那样进行约束。例如,如果文件当前未用,能删除一个数据库文件。

SQL Server CE能回收数据库文件里的空间。但它不能通过数据操作语言达到,也不能作为后台处理。为了回收SQL Server CE数据库的空间,必须使用SqlCeEngine类的Compact方法。

11.2  SQL Server CE语法

SQL Server CE编程比SQL Server编程简单,因为SQL Server CE支持的SQL语言是SQL Server的一个子集。因为SQL Server CE数据库是一个单用户数据库,它的安全通过一个文件密码控制,所以没必要有GRANTDENYPEVOKE语句。

SQL Server CE也缺少SQL ServerTransact SQL扩展:没有存储过程、语句批处理或DECLARESETIFWHILE语句。甚至一些标准的SQL也被去掉了,如不支持视图。不是所有的SQL Server数据类型都被包含,但是大多数缺少的数据类型都被转换成存在的一些类型。如Windows CE本身只支持Unicode,因此Unicode是惟一的字符数据类型。

SQL Server CE提供表、索引、默认值和参考完整性的支持。它也具有使用标准SQL数据操作语言(DML)在表中增加、修改和删除行的能力。因此,程序连接到数据库文件,提交INSERTUPDATEDELETE语句到数据库,能操作SQL Server CE的数据。读者可以自己写这些DML语句,或者用ADO.NET写它们,这是数据集同步到数据库的一个步骤。同样,SQL语句使用单引号描述字符串,当在代码引用SQL时,这是很方便的,因为避免了双引号的麻烦。

SQL Server CE程序可能比典型的SQL Server程序更基本,但是它应该提供了足够的功能去用于移动情况。Windows CE设备的可用内存是一个不可避免的限制,抛开这个尺寸限制,SQL Server CE为任何Windows CE程序提供足够的SQL命令集。

11-1和表11-2分别列出了SQL Server CE支持和不支持的SQL Server的功能。

11-1                                 SQL Server CE不支持的SQL Server功能

   

   

DCLData Control Language
GRANT
REVOKEDENY

在单用户数据库中不需要

DDLData Definition Language
视图、触发器、存储过程、用户定义函数、用户定义类型

这些大多数是对ANSI功能的SQL Server Transact SQL扩展,逻辑上,能位于SQL Server上。当运行在SQL Server CE时它必须编码到程序中

DMLData Manipulation Language
IF-ELSE
WHILEDECLARESET

这些大多数是对ANSI功能的SQL Server Transact SQL扩展

INFORMATION_SCHEMA TABLES

这个替换成了MSysObjects MSysConstrains

11-2                                   SQL Server CE支持的SQL Server功能

   

   

DDL
数据库、表、数据类型、索引、约束

只支持Unicode字符类型

DML
SELECT
INSERTUPDATEDELETE

 

函数
Aggregate
MathDateTimeStringSystem

 

事务

事务分级别始终是READ COMMITTED,嵌套的限制是5,惟一的锁粒度是表级别,在事务期间有效。单向提交是惟一允许的提交类型

 

SQL Server CE语法比SQL Server烦杂些。有一个更小的footprint,意味着有更少的代码去“推断”想要的东西。如下面的SQL,在最后列定义和约束定义间缺少一个逗号,在SQL Server 2000中执行没有问题,但是在SQL Server CE中执行会产生一个语法错误。

 

CREATE TABLE Products

            ( ProductID integer not null primary key

            , ProductName nchar(20) not null

            , CategoryID integer not null

              CONSTRAINT FKProductCategory foreign key (CategoryID)

                                      references Categories(CategoryID)

            )

 

11-2  使用别名列名的
DataGrid
控件

 

列名在SELECT语句中能用别名。这些别名默认地反馈到数据对象和绑定的控件上。这样,下列的SELECT语句,填充一个绑定到DataGrid控件的DataTable对象,如图11-2所示的结果。

 

SELECT P.ProductID as ID

      , P.ProductName as Name

      , C.CategoryName as Category

  FROM Products P

  JOIN Categories C on C.CategoryID = P.CategoryID

 

对于指定一个DataGrid类的类名它并不是首选。使用DataGridColumnStyle对象更好些,因为它与底层的SELECT语句无关。

11.3  SQL Server CE Query Analyzer

浏览和查询一个SQL Server CE数据库的主要工具是SQL Server CE Query Analyzer。像SQL Server 2000Query Analyzer一样,SQL Server CE Query Analyzer提供了一个创建和提交特定查询的便利途径。

SQL Server CE Query Analyzer的安装依赖于安装SQL Server CE的开发环境。当安装SQL Server CE时,SQL Server CE Query Analyzer默认地不被安装到设备上。一旦Query Analyzer安装到设备上,才能直接从开始菜单或安装目录运行可执行文件Isqlw20.exe。单击文件管理器中的一个数据库文件也能打开Query Analyzer

Query Analyzer允许查看数据库的结构信息,如图11-3所示,以及提交对那个数据库的查询,如图11-4所示。

                  

           11-3  SQL Server CE Query                  11-4  SQL Server CE Query
            Analyzer
Objects选项卡                       AnalyzerSQL选项卡

Query Analyzer窗体显示一个最小化框,不是一个关闭框。如果使用Query Analyzer去查看数据库,然后在最小化框上单击,那么Query Analyzer将消失,但是没有关闭,也不关闭正在显示的数据库。任何试图访问该数据库的其他程序只能等到关闭与Query Analyzer连接的数据库(在Objects选项卡,在有数据库名称的树节点上单击,然后在工具条上单击Stop按钮)或关闭Query Analyzer(选择Tools Exit)才能访问。

11.4  创建一个SQL Server CE数据库

访问SQL Server CE的第一个程序位于CreateDatabase项目,程序用于创建和组装一个数据库。

使用System.Data.SqlServerCe命名空间的Engine类创建一个SQL Server CE数据库。因为每个SQL Server CE数据库都是一个单个文件,要做的就是告诉Engine对象那个文件的路径和名称。也能在Engine类的构造函数或设置对象LocalConnectionString属性中告之路径和文件名。在任一情况中,必须在执行Engine类的CreateDatabase方法前指定文件名,因为这个方法不带任何参数。

创建SQL Server CE数据库的代码如图11-5所示。

11-5  CreateDatabase程序

private string  strFile = @"My Documents/ourProduceCo.sdf";

private string  strConn = "Data Source=" +

                               @"My Documents/ourProduceCo.sdf";

 

 

private void mitemCreateDB_Click(object sender, EventArgs e)

{

   if ( File.Exists(strFile) ) { File.Delete(strFile); }

 

   SqlCeEngine dbEngine = new SqlCeEngine();

   dbEngine.LocalConnectionString = strConn;

   try

   {

      dbEngine.CreateDatabase();

   }

   catch( SqlCeException exSQL )

   {

      MessageBox.Show("Unable to create database at " +

                          strFile +

                          ". Reason:  " +

                          exSQL.Errors[0].Message );

   }

}

 

虽然Engine类有一个CreateDatabase方法,但是它没有DropDatabase方法。删除一个数据库必须通过提交一个DROP DATABASE语句到SQL Server CE数据库或使用删除文件的File类来完成。

11.5  组装一个SQL Server CE数据库

现在已经创建了SQL Server CE数据库,接下来就能用表、索引和数据组装它。读者可能认为,用Engine类完成这个操作,但Engine只操作一个完整的数据库,不操作数据库内的单独组件,如表。使用标准的SQL DDL语句,例如CREATE TABLECREATE INDEX,以及标准的SQL DML语句INSERTUPDATEDELETE组装SQL Server CE数据库。

提交SQL语句到数据库需要两个类,一个打开连接,一个提交语句,分别是SqlCeConnectionSqlCeCommand类。

SqlCeConnection类打开一个数据库的连接,这需要数据库文件的名称。SqlCeCommand类使用Execute方法提交一次一个SQL语句到数据库,该方法需要使用连接对象和要提交的SQL语句。SqlCeConnectionDML语句是SqlCeCommand对象的属性。在命令的Execute方法调用之前必须设置它们,类连接必须打开。表11-3描述了SQLCeComnection类的执行方法。

11-3                                            SqlCeCommand类的执行方法

   

   

ExecuteNonQuery

执行一个SQL语句,不返回行,例如INSERTCREATE

ExecuteScalar

执行一个SQL语句,只返回一个值,例如SELECT SUM(Value) FROM Orders WHERE CustomerID = "ABCD"

ExecuteReader

执行一个SQL语句,返回多列或多行

 

下面显示的创建两个简单表的代码:

 

private void mitemCreateTables_Click(object sender, EventArgs e)

{

   SqlCeConnection  connDB = new SqlCeConnection();

   SqlCeCommand  cmndDB = new SqlCeCommand();

 

   connDB.ConnectionString = strConn;

   connDB.Open();

 

   cmndDB.Connection = connDB;

 

   cmndDB.CommandText =

      " CREATE TABLE Categories " +

      "  ( CategoryID integer not null " +

      "           CONSTRAINT PKCategories PRIMARY KEY " +

      "  , CategoryName nchar(20) not null " +

      "  )";

   cmndDB.ExecuteNonQuery();

 

   cmndDB.CommandText =

      " CREATE TABLE Products " +

      "  ( ProductID integer not null " +

      "           CONSTRAINT PKProducts PRIMARY KEY " +

      "  , ProductName nchar(20) not null " +

      "  , CategoryID integer not null " +

      "  , CONSTRAINT FKProdCat " +

      "        foreign key (CategoryID) " +

      "        references Categories(CategoryID) " +

      "  )";

   cmndDB.ExecuteNonQuery();

 

   connDB.Close();

}

 

SQL Server CE为代码指定的3个约束自动地创建索引,只需要为不包含约束的索引提交CREATE INDEX语句就可以了。

下面显示的是插入行到表的代码:

 

private void mitemLoadData_Click(object sender, EventArgs e)

{

   SqlCeConnection  connDB = new SqlCeConnection();

   SqlCeCommand  cmndDB = new SqlCeCommand();

 

   connDB.ConnectionString = strConn;

   connDB.Open();

 

   cmndDB.Connection = connDB;

   cmndDB.CommandText =

      " INSERT Categories " +

      "   (CategoryID, CategoryName)" +

      "   VALUES (1, 'Franistans' )";

   cmndDB.ExecuteNonQuery();

   cmndDB.CommandText =

      " INSERT Categories " +

      "   (CategoryID, CategoryName)" +

      "   VALUES (2, 'Widgets' )";

   cmndDB.ExecuteNonQuery();

 

   cmndDB.CommandText =

      " INSERT Products " +

      "    (ProductID, ProductName, CategoryID)" +

      "    VALUES (11, 'Franistans - Large', 1 )";

   cmndDB.ExecuteNonQuery();

   cmndDB.CommandText =

      " INSERT Products " +

      "    (ProductID, ProductName, CategoryID)" +

      "    VALUES (12, 'Franistans - Medium', 1 )";

   cmndDB.ExecuteNonQuery();

   cmndDB.CommandText =

      " INSERT Products " +

      "    (ProductID, ProductName, CategoryID)" +

      "    VALUES (13, 'Franistans - Small', 1 )";

   cmndDB.ExecuteNonQuery();

   cmndDB.CommandText =

      " INSERT Products " +

      "    (ProductID, ProductName, CategoryID)" +

      "    VALUES (21, 'Widgets - Large', 2 )";

   cmndDB.ExecuteNonQuery();

   cmndDB.CommandText =

      " INSERT Products " +

      "    (ProductID, ProductName, CategoryID)" +

      "    VALUES (22, 'Widgets - Medium', 2 )";

   cmndDB.ExecuteNonQuery();

   cmndDB.CommandText =

      " INSERT Products " +

      "    (ProductID, ProductName, CategoryID)" +

      "    VALUES (23, 'Widgets - Small', 2 )";

   cmndDB.ExecuteNonQuery();

 

   connDB.Close();

}

 

因为代码的SQL语句没有数据返回,所以使用ExecuteNonQuery方法。

这样,两个类、很少的方法和属性、一些标准SQL,就组装了一个数据库。这是一个简单的数据库,但是接下来将使用同样的类、属性和方法产生一个更复杂的数据库。

11.6  接收和显示数据

现在已经用一些数据组装了SQL Server CE数据库,接下来将考虑如何显示数据给用户。下面由两层程序开始,使用SqlCeDataReader类,然后移到3层程序。

SqlCeDataReader类允许程序访问一个SQL查询返回的行。它一次只提供对一行只读的向前访问。一旦定位到一行,使用DataReader类的方法去访问位于那行的各个字段的信息。这些方法中许多函数的名字里都以Get开头,字段是0开始的数组。这样,如果底层SELECT语句是SELECT ProductIDProductName FROM Products,那么接收来自DataReader对象当前行的ProductID字段(字段0)的代码如下:

 

intVar = drdrDataReader.GetInt32(0);

 

//  或明显低效的

 

intVar = drdrDataReader.GetValue(0);

 

// 

 

intVar = drdrDataReader.Item["ProductID"];

 

同样,接收ProductName 字段将是下列代码:

 

strVar = drdrDataReader.GetString(1);

 

// 

 

strVar = drdrDataReader.GetValue(1);

 

// 

 

strVar = drdrDataReader.Item["ProductName"];

 

不能直接创建一个DataReader对象,必须使用SqlCeCommand对象的ExecuteReader方法去创建DataReader对象。当第一次创建时,DataReader被打开,但是仍没有定位到一行,有两种方法定位到一行。

第一种使用DataReader对象的Read方法去移动行。第二种是使用DataReader对象的Seek方法去定位到指定的行。

n     底层数据表是有索引的。

n     在执行ExecuteReader方法前设置了SqlCeCommand对象的额外属性。

n     调用Seek方法指定索引名称和关键字值。

n     调用Read方法之后跟着调用Seek方法。

11-6  DataReader程序

 

使用Seek方法的好处是高性能。实际上,从NETCompact Framewigk.1版本开始.NET Compact Framework开发组加Seek方法到产品中性能是一个很重要的原因。使用Seek方法也始终比使用定位到指定行的SELECT语句的WHERE子句快很多。缺点就是Seek方法一次只能访问一个表,而且索引是必需的。这就是意味着不能结合Seek方法联合两个或更多的表。这与关系型数据库理论格格不入。

下面例子是为了示例ReadSeek方法,如图11-6所示。ComboBox控件装载产品关键值。当用户单击ComboBox控件,选择产品的字段显示在TextBox控件中。

程序代码如下。

 

using System;

using System.IO;

using System.Drawing;

using System.Collections;

using System.Windows.Forms;

using System.Data;

using System.Data.Common;

using System.Data.SqlServerCe;

 

namespace DataReader

{

   ///

   /// Summary description for FormMain

   ///

   public class FormMain : System.Windows.Forms.Form

   {

     internal System.Windows.Forms.Label lblCategoryName;

     internal System.Windows.Forms.Label lblProductName;

     internal System.Windows.Forms.Label lblProductID;

     internal System.Windows.Forms.TextBox textCategoryName;

     internal System.Windows.Forms.TextBox textProductName;

     internal System.Windows.Forms.TextBox textProductID;

     internal System.Windows.Forms.ComboBox comboKeys;

 

     public FormMain()

       {

         //

         // Required for Windows Form Designer support

         //

         InitializeComponent();

      }

      ///

      /// Clean up any resources being used.

      ///

      protected override void Dispose( bool disposing )

      {

         base.Dispose( disposing );

      }

      #region Windows Form Designer generated code

 

 

      ///

      /// The main entry point for the application

      ///

 

      static void Main()

      {

         Application.Run(new FormMain());

      }

      //  文件路径和名称

      private string  strFile = @"My Documents/ourProduceCo.sdf";

 

      //  连接字符串

      private string  strConn = "Data Source=" +

                                      @"My Documents/ourProduceCo.sdf";

 

      // 选择产品主键

      private string strGetProductIDs =

         " SELECT ProductID " +      "   FROM Products ";

 

      //  选择一个产品,与分类关联

      private string strGetOneProduct =

         " SELECT ProductID, ProductName, CategoryName " +

         "   FROM Products P " +

         "   JOIN Categories C on C.CategoryID = P.CategoryID " +

         "  WHERE P.ProductID = ";

 

      //  ComboBox装载时使用SelectIndexChanged 事件

       private bool boolLoading = true;

 

      private void FormMain_Load(object sender, EventArgs e)

      {

         //  显示关闭框

         this.MinimizeBox = false;

 

         Application.DoEvents();

 

         //  确保数据库存在

         if (! File.Exists(strFile) )

         {

            MessageBox.Show(

               "Database not found.  Run the CreateDatabase " +

               "program for this chapter first.  Then run " +

               "this program.");

         }

 

         // 装载产品主键到ComboBox

         // 并选择第一个

         LoadProductIDs();

         comboKeys.SelectedIndex = 0;

      }

 

      private void comboKeys_SelectedIndexChanged(object sender,

                                                            EventArgs e)

      {

         // 已经选择一个产品主键;接收并显示相关的产品

         if (! boolLoading )

         {

            LoadOneProduct((int)comboKeys.SelectedItem);

         }

      }

 

      private void textProductName_Validated(object sender,

                                                      EventArgs e)

      {

         //  在数据库中更新这个产品行

         UpdateSelectedRow(int.Parse(textProductID.Text),

                                textProductName.Text);

      }

 

      private void LoadProductIDs()

      {

         //  清空ComboBox.

         comboKeys.Items.Clear();

 

         //  A connection, a command, and a reader

         SqlCeConnection connDB =

            new SqlCeConnection(strConn);

         SqlCeCommand cmndDB =

            new SqlCeCommand(strGetProductIDs, connDB);

         SqlCeDataReader drdrDB;

 

         //  打开连接

         connDB.Open();

 

         //  提交SQL语句并接收SqlCeReader作为结果集

         drdrDB = cmndDB.ExecuteReader();

 

         //  读每行。只添加列内容作为ComboBox的条目

         //  完成时关闭reader

         while ( drdrDB.Read() )

         {

            comboKeys.Items.Add(drdrDB["ProductID"]);

         }

         drdrDB.Close();

 

         //  关闭连接

         connDB.Close();

 

         // 开始响应ComboBoxSelectedIndexChanged事件

         this.boolLoading = false;

      }

 

      private void LoadOneProduct( int intProductID)

      {

         //  A connection, a command, and a reader

         SqlCeConnection  connDB = new SqlCeConnection(strConn);

         SqlCommand  cmndDB = new SqlCommand(strSQL,connDB);

         SqlCeDataReader drdrDB;

 

         //  打开连接

         connDB.Open();

 

         // 设置Command对象去接收通过索引来自一个表的行

         //  然后接收reader.

         cmndDB.Connection = connDB;

         cmndDB.CommandType = CommandType.TableDirect;

         cmndDB.CommandText = "Products";

         cmndDB.IndexName = "PKProducts";

         drdrDB = cmndDB.ExecuteReader();

 

         //  只接收Products 表中具有ComboBox选择的ProductID的第一行

         //  装载字段到窗体的控件

         //  关闭reader.

         drdrDB.Seek(DbSeekOptions.FirstEqual, intProductID);

            if( drdrDB.Read() )

            {

               LoadControlsFromRow(drdrDB);

            }

         drdrDB.Close();

 

         //  Close the connection.

         connDB.Close();

      }

 

 

      private void LoadControlsFromRow(  SqlCeDataReader drdrDB)

      {

         //  Transfer the column titles and the field

         //     contents of the current row from the

         //     reader to the form's controls.

         lblProductID.Text = drdrDB.GetName(0);

         textProductID.Text = drdrDB.GetValue(0).ToString();

         lblProductName.Text = drdrDB.GetName(1);

         textProductName.Text = drdrDB.GetValue(1).ToString();

         lblCategoryName.Text = drdrDB.GetName(2);

         textCategoryName.Text = drdrDB.GetValue(2).ToString();

      }

 

      private void UpdateSelectedRow(int intProductID,

                                           string strProductName)

      {

         //  A connection and a command

         SqlCeConnection connDB = new SqlCeConnection(strConn);

         SqlCeCommand cmndDB = new SqlCeCommand();

 

         //  打开连接

         connDB.Open();

 

         // 为选择的产品更新产品名称

         cmndDB.Connection = connDB;

         cmndDB.CommandText =

            " UPDATE Products " +

            " SET ProductName = " + "'" + strProductName + "'" +

            " WHERE ProductID = " + intProductID;

         cmndDB.ExecuteNonQuery();

 

         //  关闭连接

         connDB.Close();

      }

   }

}

 

LoadProductIDs函数提交一个SELECT语句,使用一个DataReader对象去处理SELECT语句返回的所有行。注意,SqlCeCommand对象的所有属性必须在DataReader对象创建前初始化。因为当创建时不定位到第一行,必须在迭代循环开始时调用Read方法,而不是循环结束。这有助于更清晰的代码,是对原来的ADO.NET类的一个改善。

11-7  使用另一个LoadOne
Product
方法的输出

 

 

 

LoadOneProduct函数在Read方法之后接着使用Seek方法去接收来自数据的单个行。注意,调用Seek方法之后也必须调用Read方法。不调用Read方法,将没有数据在reader中。如果Read方法没被调用,尽管没有行在reader中,但是也没有异常抛出,只是简单的有一个空的reader

如图11-7所示的输出。所有列来自Products表,使用的是一个内建的Seek方法。下面代码显示的是LoadOneProduct方法的另一个版本。它使用WHERE子句而不是Seek方法索引去指定想要的产品。输出如图11-7所示,这个版本的LoadOneProduct方法能从多个表中获得字段,较多的是CategoryName字段与每个产品关联,较少的是CategoryID字段关联。然而,前面也提到了,这个版本的方法执行比较慢。

 

private void LoadOneProduct( int intProductID)

{

   //  追加想要ProductIDSELECT语句

    string  strSQL = strGetOneProduct + intProductID;

 

   //  A connection, a command, and a reader

   SqlCeConnection  connDB = new SqlCeConnection(strConn);

   SqlCommand  cmndDB = new SqlCommand(strSQL,connDB);

   SqlCeDataReader drdrDB;

 

   // 打开连接

   connDB.Open();

 

   //  提交SQL语句,接收SqlCeReader 作为单行结果集

   drdrDB = cmndDB.ExecuteReader();

 

   //  只读第一行。显示它。关闭reader.

   if ( drdrDB.Read() )

   {

      LoadControlsFromRow(drdrDB);

   }

   drdrDB.Close();

 

   //  关闭连接

   connDB.Close();

}

 

前面提到,从ExecuteReader方法返回的DataReader对象不定位到第一行。但是它已经被打开,必须关闭它。不像桌面的位于多用户环境的DataReader对象,SqlCeDataReader对象是为移动设备而写的。这意味着在同一连接上一次能有多个reader。这是一个必需的性能,允许每个数据库一次只有一个打开的SqlCeConnection对象。

DataReader对象的当前行和DataRow对象不是一样的。DataRow是一个到自身的对象,DataReader的当前行不是。如前面所述,使用DataReader本身的方法和属性访问DataReader对象的当前行的内容。

11.7  更新一个SQL Server CE数据库

现在,已经将数据显示给用户,接下来将继续允许用户修改数据并且提交这些修改数据到数据库。当第一次组装数据库时,已经给出了一个例子。下面是已经介绍过的类似代码例子。

使用textProductName.Validated事件允许用户改变产品名称的程序,代码如下:

 

private void textProductName_Validated(object sender, EventArgs e)

{

    //  在数据库中更新这个产品行

    UpdateSelectedRow(int.Parse(textProductID.Text), textProductName.Text);

}

 

下面代码显示的是更新产品行的调用函数。

 

private void UpdateSelectedRow(int intProductID,

                                     string strProductName)

{

   //  A connection and a command

   SqlCeConnection connDB = new SqlCeConnection(strConn);

   SqlCeCommand cmndDB = new SqlCeCommand();

 

   //  打开连接

   connDB.Open();

 

   //  更新选择的产品的名称

   cmndDB.Connection = connDB;

   cmndDB.CommandText =

      " UPDATE Products " +

      " SET ProductName = " + "'" + strProductName + "'" +

      " WHERE ProductID = " + intProductID;

   cmndDB.ExecuteNonQuery();

 

   //  关闭连接

   connDB.Close();

}

 

如果程序允许多个字段被更新,应该在每个字段的Validated事件处理中设置一个“需要更新”的标志,如必要可反馈到ComboBox控件的SelectedIndexChanged和窗体的Close事件上。

如果这个程序只是访问一个数据库的程序,不必每次提交一个查询到数据库就打开和关闭连接。在这种情况,将OpenClose调用移到窗体的LoadClosed事件中。

通过学习已经知道,如何使用SqlCeConnectionSqlCeCommandSqlCeDataReader类接收来自一个SQL Server CE数据库的数据,并在窗体的控件上表现数据给用户,从而用户接收改变,提交这些改变给数据库。并且,这章也讲了两层程序中SQL Server CE的介绍。

 

你可能感兴趣的:(windows,数据库,程序开发,嵌入式,sql,server,insert)