使用托管 C++ 进行 ADO.NET 入门






4.24/5 (12投票s)
2002年7月31日
9分钟阅读

265696
使用 Visual C++ .NET 的托管扩展来使用 ADO.NET 的入门指南
摘要
本文旨在介绍使用托管 C++ 的 ADO.NET 进行数据访问和操作。提供的代码示例展示了如何使用 DataReader
、DataSet
、DataAdapter
和 DataRelation
来创建 SQL Server 解决方案。本文面向具有一定托管 C++ 编码知识的初学者。
您会发现,一旦开始使用新的对象模型,它就与 ADO 中传统的面向连接的数据访问技术完全不同。新的数据访问设计非常适合分层应用程序设计,减少了在数据源上连接和锁定资源的需求。
在开始使用这些对象之前,需要理解它们之间的关系。您可以将架构中的对象视为堆叠在一起的层,为上层提供支持。DataSet
由 DataAdapter
支持,DataAdapter
包含一些属性,这些属性通过命令对象定义,而命令对象又位于到数据源的连接之上。
虽然我将架构描述为层是一种非常简化的视图,但 ADO.NET 虽然与 ADO 不同,但并非一定更复杂。
ADO.NET 的优点在于它遵循了我们使用 ADO 的方向进行设计。也就是说,断开连接的记录集、数据整形以及用于跨平台集成的 XML。
ADO.NET 中不包含记录集和游标的概念;取而代之的是用于处理数据的新对象:DataSets
、DataAdapters
和 DataReader
。
一本好的 ADO.NET 入门指南很容易就能填满一本书。这篇七页的文章无法让 ADO.NET 得到充分的介绍。我希望在阅读完本文后,您能对 ADO.NET 框架有一个很好的理解,并能够开始使用它。
本文分为几个部分。第一部分讨论了架构。第二部分将引导您了解一些代码片段。最后一部分是结论和关于我们所讲主题的一些代码示例。
您需要 Visual C++.NET 来编译示例。示例使用 Pubs 数据库连接到 SQL Server。要测试代码,您需要 SQL Server 和 Pubs 数据库。
ADO.NET 架构概述
ADO.NET 架构与 ADO 完全不同。新的对象模型被设计成一种断开连接的数据访问方法。在实现层面,最明显的变化是框架中缺少记录集和游标。一旦我们克服了如何使用记录集和游标工作的障碍,我们就能看到在分层设计中使用 ADO.NET 的优势。
在开始看代码之前,我将简要介绍一下新框架的架构。
该架构由两个元素组成:.NET 数据提供程序和 DataSet
。.NET 数据提供程序的组件为我们提供了连接和处理数据源的机制。DataSet
组件以类似 ADO 中断开连接的记录集的结果集形式,为我们提供数据源的内存表示。
.NET 数据提供程序库针对数据库平台。本文的实现部分将简要介绍不同的库以及您可能需要在项目中找到的库。
.NET 数据提供程序组成的组件是 Connection
、Command
、DataReader
和 DataAdapter
。DataSet
中的组件是用于结果集(表)、行、列、约束和关系 的集合对象。
DataAdapter
位于 DataSet
和 Command
对象之间。DataAdapter
的目的是提供结果集与数据源的交互。DataAdapter
有四个属性决定了交互是如何进行的。这些属性是 SelectCommand
、InsertCommand
、UpdateCommand
和 DeleteCommand
。这些属性持有对 Command
对象实例的引用。
DataAdapter
使用 SelectCommand
属性填充 DataSet
中的结果集。一旦调用 Update 方法,DataAdapter
就会分析结果集中的更改,并发出命令属性中的相应命令。如果 DataAdapter
分析了更改但未定义命令属性,则会引发异常。
DataReader
对象用于尽快地从连接中检索数据。数据以向前、只读流的形式提供,所需内存仅用于包含当前记录。
Connection
和 Command
对象不言自明。Command
对象定义了一个 SQL 命令。SQL 命令可以直接针对连接执行,或者由 DataAdapter
引用。直接执行的任何结果都可以通过 DataReader
进行处理。
Connection
对象定义了与数据源的连接。Connection
对象和 DataReader
是完成后需要关闭的唯一对象。
DataSet
DataSet
由暴露表、行和列的集合组成。表集合通常称为结果集,表示为 DataTable
对象。每个结果集都需要自己的 DataAdapter
来维护结果集到源表的更改。
集合中的结果集、行和列通过索引访问,并且表的、行或列的内存操作使用 DataTable
、DataRow
、DataColumn
和 DataRelation
对象完成。
最终,DataSet
无法访问数据库中的底层记录。它只包含 DataAdapter
提供的一个或多个数据副本。DataAdapter
负责将数据修改传达给数据源。
DataRelation
作为 DataSet
的一部分,DataRelation
对象定义了 DataSet
结果集之间的关系。定义关系会创建一个层次结构数据集。
处理层次结构结果集需要设置 DataRow
集合,通过调用 GetChildRows
方法来检索子记录列表。这听起来比实际要复杂。我将在下一节中为您演示一个 DataRelation
示例。
ADO.NET 的实现
要开始使用 ADO.NET,您需要在源代码中引用 System.Data.dll 库。如果使用 DataSet
,您还需要引用 System.Xml.dll。
#using <system.data.dll> // This is required for the ADO.NET Provider
#using <System.Xml.dll> // This is only required for the DataSet
using namespace System::Data;
using namespace System::Data::SqlClient;
using namespace System::Xml; // This is required for the DataSet
针对各种数据源有不同的 .NET 提供程序。下表描述了您的目标源所需的命名空间。
提供程序命名空间 |
目标源 |
注释 |
|
SQL Server 7 及更高版本,包括 MSDE 引擎 |
|
|
Microsoft SQL Server 6.5 及更早版本,任何其他支持 OLE DB 的平台。Microsoft Access。 |
|
|
ODBC 兼容数据源 |
需要下载 |
|
Oracle 数据源 |
需要下载 |
在添加引用并使用命名空间设置范围后,我们可以开始使用该框架连接到源。
SqlConnection * mySQLConnection;
mySQLConnection = new SqlConnection
(S"server=local;Trusted_Connection=yes;database=pubs;");
mySQLConnection->Open (); // Open up the connection
SqlCommand
对象设置起来同样简单。
SqlCommand * mySQL;
mySQL = new SqlCommand (S"select * from authors", mySQLConnection);
设置 DataReader
并开始提取记录也很简单。
SqlDataReader * myReader;
myReader = mySQL->ExecuteReader ();
while(myReader->Read ())
Console::WriteLine(myReader->get_Item ("au_lname")->ToString ());
完成使用 DataReader
和 Connection 后,应通过调用 Close 方法关闭它们。
myReader->Close ();
mySQLConnection->Close();
在设置 DataSet
之前,我们需要创建一个 DataAdapter
。DataAdapter
用于使用结果集填充 DataSet
,并允许将对结果集的更改应用回源。以下代码片段演示了如何创建 DataAdapter
并设置 SelectCommand
属性。
SqlDataAdapter * myDataAdapter;
myDataAdapter = new SqlDataAdapter();
myDataAdapter->SelectCommand = new SqlCommand
(S"select * from authors",mySQLConnection);
正如您所见,SelectCommand
属性引用了一个包含 SQL Select 命令的 SqlCommand
对象。此属性用于使用 Select 的结果填充 DataSet
。创建 DataSet
并用结果集填充它的代码如下:
DataSet * myDataSet;
myDataAdapter->Fill (myDataSet,"authors");
// DataSet is populated using the Select * from authors command
由于 DataAdapter
负责进行源更新,因此我们必须设置适当的命令属性以允许更新。DataAdapter
根据结果集中的更改来决定对源执行哪个属性。
对于您要进行更新的每个源表,都需要一个 DataAdapter
。
以下代码示例显示了如何为新记录更新准备 DataAdapter
的 InsertCommand
属性。该示例使用了参数集合。
SqlParameter * myParameter;
myDataAdapter->InsertCommand = new SqlCommand(S"insert into authors " +
"(au_id,au_lName,au_fname,contract) values @auID, " +
"@l_name, @f_name, @contract)", mySQLConnection);
//Set up the four parameters
//Au_id parameter
myParameter = myDataAdapter->InsertCommand->Parameters->Add(
new SqlParameter ("@auID", SqlDbType::VarChar));
myParameter->SourceColumn = "au_id";
myParameter->SourceVersion = DataRowVersion::Current;
// l_name parameter
myParameter = myDataAdapter->InsertCommand->Parameters->Add(
new SqlParameter("@l_name", SqlDbType::VarChar));
myParameter->SourceColumn = "au_lname";
myParameter->SourceVersion = DataRowVersion::Current;
// f_name parameter
myParameter = myDataAdapter->InsertCommand->Parameters->Add(
new SqlParameter("@f_name", SqlDbType::VarChar));
myParameter->SourceColumn = "au_fname";
myParameter->SourceVersion = DataRowVersion::Current;
// contract parameter
myParameter = myDataAdapter->InsertCommand->Parameters->Add(
new SqlParameter("@contract", SqlDbType::Bit));
myParameter->SourceColumn = "contract";
myParameter->SourceVersion = DataRowVersion::Current;
在设置 InsertCommand
属性后,就可以向数据源添加新记录了。调用 DataAdapter
上的 Update 方法将触发 DataAdapter
分析结果集中的更改,并从命令属性中发出相应的命令。在此示例中,DataAdapter
将决定使用 InsertCommand
属性进行更新。下面显示了创建新记录到作者结果集和源表的代码。
DataRow * myRow;
myRow = myDataSet->Tables->Item["authors"]->NewRow ();
myRow->Item [0]=S"123-45-6799"; // Key
myRow->Item [1]=S"Bill"; // au_fname field
myRow->Item [2]=S"Ferreira"; // au_lname field
myRow->Item [8]=S"true"; // contract field
myDataSet->Tables->Item ["authors"]->Rows->Add (myRow);
// The DataAdapter will analyze the update as requiring an insert and
// use the insert from the insertcommand property.
myDataAdapter->Update (myDataSet,"authors");
完成连接后,您需要将其关闭。
mySQLConnection->Close();
如果我们删除结果集中的一条记录,DataAdapter
的 Update 方法将抛出异常,因为 DeleteCommand
属性未初始化任何 SQL Delete 命令。
使用 DataRelation
对象可以轻松地在多个结果集之间创建关系。此代码片段显示了如何使用 DataRelation
对象在两个结果集之间创建单个关系。
DataRelation * myRelation;
myRelation = myDataSet->Relations->Add ("titleauthers",
myDataSet->Tables->Item ["authors"]->Columns->Item ["au_id"],
myDataSet->Tables->Item ["titles"]->Columns->Item ["au_id"]);
当您遍历作者结果集时,您将调用 GetChildRows
方法来获取所有子行。
DataRow * myAuthorRow;
DataRow * myTitlesRow[];
for(inti=0;>= myDataSet->Tables->Item ["authors"]->Rows->Count; i++) {
myAuthorRow = myDataSet->Tables->Item ["authors"]->Rows->Item[i];
Console::WriteLine("Author ID : {0}",myAuthorRow->Item ["au_id"]->ToString ());
myTitlesRow = myAuthorRow->GetChildRows (myRelation);
for(intx=0; x < myTitlesRow->Count ;x++)
Console::WriteLine("Titles ID : {0}",
myTitlesRow[x]->Item ["title_id"]->ToString ());
}
结论
我希望本文对您有所帮助。我添加了一些在 ADO.NET 编程中有用的参考资料。本文涵盖的内容仅触及 ADO.NET 强大功能和灵活性的表面。
附带了四组完整的示例,我选择这些示例来演示本文讨论的对象。
第一个示例创建并实例化了连接和命令对象。在 DataReader
上执行命令对象,并将结果提取到控制台。
第二个示例创建并实例化了 Connection
、Command
、DataAdapter
和 DataSet
对象。循环 DataSet
对象以显示字段的内容。
第三个示例显示了新记录的插入。
第四个示例显示了如何使用 DataRelation
对象来创建 DataSet
的两个结果集之间的关系。
对于熟悉 ADO.NET 的人来说,您可能会同意,入门指南应该涵盖更多内容。我需要坚持基本知识,否则它就会变成一个没有范围的开发项目,整个世界都会被卷入其中。
如果您对本文有任何反馈,我将不胜感激。可以通过电子邮件与我联系:gbferreira@hotmail.com。
参考文献
- ADO.NET 示例 http://samples.gotdotnet.com/quickstart/latebreaking/
- 使用 ADO.NET 的最佳实践 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnmqqc/html/msmqbest.asp
- .NET Framework SDK 文档
源代码
// This is the main project file for VC++ application project
// generated using an Application Wizard.
#include "stdafx.h"
// Standard
#using <mscorlib.dll>
#using <System.dll>
#using <system.data.dll> // This is required for the ADO.NET Provider
#include <tchar.h>
using namespace System;
using namespace System::Data;
using namespace System::Data::SqlClient;
// This is the entry point for this application
int _tmain(void)
{
SqlConnection * mySQLConnection;
SqlCommand * mySQL;
SqlDataReader * myReader;
try
{
mySQLConnection = new SqlConnection(
S"server=local;Trusted_Connection=yes;database=pubs;");
mySQL = new SqlCommand (S"select * from authors",
mySQLConnection);
mySQLConnection->Open (); // Open up the connection
myReader = mySQL->ExecuteReader ();
while(myReader->Read ())
Console::WriteLine(myReader->get_Item("au_lname")->ToString ());
}
catch(Exception * e)
{
Console::Write(e->ToString () );
}
__finally
{
myReader->Close ();
mySQLConnection->Close();
}
return 0;
}
// This is the main project file for VC++ application project
// generated using an Application Wizard.
#include "stdafx.h"
// Standard
#using <mscorlib.dll>
#using <System.dll>
#using <system.data.dll> // This is required for the ADO.NET Provider
#using <System.Xml.dll> // This is required for the DataSet
#include <tchar.h>
using namespace System;
using namespace System::Data;
using namespace System::Xml; // This is required for the DataSet
using namespace System::Data::SqlClient;
// This is the entry point for this application
int _tmain(void)
{
SqlConnection * mySQLConnection;
SqlDataAdapter * myDataAdapter;
DataSet * myDataSet;
try
{
mySQLConnection = new SqlConnection(
S"server=local;Trusted_Connection=yes;database=pubs;");
myDataAdapter = new SqlDataAdapter();
myDataSet = new DataSet();
mySQLConnection->Open (); // Open up the connection
// Assign the SelectCommand with an SQL Select command
myDataAdapter->SelectCommand = new SqlCommand (
S"select * from authors",mySQLConnection);
// Use the DataAdapter to fill the DataSet
// A DataSet can be made up of many results. I called
// this result authors. The DataAdapter will know to use
// the SelectCommand property to populate the DataSet.
myDataAdapter->Fill (myDataSet,"authors");
for(inti=0;i < myDataSet->Tables->Item ["authors"]->Rows->Count; i++)
Console::WriteLine("Name:{0}",
myDataSet->Tables->Item ["authors"]->Rows->Item[i]->Item
["au_lname"]->ToString ());
}
catch(Exception * e) {
Console::Write(e->ToString () );
}
__finally {
mySQLConnection->Close();
}
return 0;
}
// This is the main project file for VC++ application project
// generated using an Application Wizard.
#include "stdafx.h"
// Standard
#using <mscorlib.dll>
#using <System.dll>
#using <system.data.dll>
#using <System.Xml.dll>
#include <tchar.h>
using namespace System;
using namespace System::Data;
using namespace System::Xml;
using namespace System::Data::SqlClient ;
// This is the entry point for this application
int _tmain(void)
{
SqlConnection * mySQLConnection ;
SqlDataAdapter * myDataAdapter;
DataSet * myDataSet;
DataRow * myRow;
SqlParameter * myParameter;
try
{
mySQLConnection = new SqlConnection
(S"server=local;Trusted_Connection=yes;database=pubs;");
myDataAdapter = new SqlDataAdapter();
myDataSet = new DataSet();
// Open up the connection
mySQLConnection->Open ();
// Assign the SelectCommand with an SQL select command
myDataAdapter->SelectCommand = SqlCommand (S"select * from authors",
mySQLConnection);
myDataAdapter->InsertCommand =
new SqlCommand (S"insert into authors (au_id,au_lName," +
S"au_fname,contract) values (@auID,@l_name," +
S"@f_name,@contract)",mySQLConnection);
//Au_id parameter
myParameter = myDataAdapter->InsertCommand->Parameters->Add(
new SqlParameter("@auID", SqlDbType::VarChar));
myParameter->SourceColumn = "au_id";
myParameter->SourceVersion = DataRowVersion::Current;
// l_name parameter
myParameter = myDataAdapter->InsertCommand->Parameters->Add(
new SqlParameter("@l_name", SqlDbType::VarChar));
myParameter->SourceColumn = "au_lname";
myParameter->SourceVersion = DataRowVersion::Current;
// f_name parameter
myParameter = myDataAdapter->InsertCommand->Parameters->Add(
new SqlParameter("@f_name", SqlDbType::VarChar));
myParameter->SourceColumn = "au_fname";
myParameter->SourceVersion = DataRowVersion::Current;
// contract parameter
myParameter = myDataAdapter->InsertCommand->Parameters->Add(
new SqlParameter("@contract", SqlDbType::Bit));
myParameter->SourceColumn = "contract";
myParameter->SourceVersion = DataRowVersion::Current;
// Use the DataAdapter to fill the DataSet
// A DataSet can be made up of many results. This result set I
// called authors.
// The DataAdapter will know to use the SelectCommand object
// to populate the DataSet.
myDataAdapter->Fill (myDataSet,"authors");
myRow = myDataSet->Tables->Item["authors"]->NewRow ();
myRow->Item [0]=S"123-45-6799"; // Key
myRow->Item [1]=S"Bill"; // au_fname
myRow->Item [2]=S"Ferreira"; // au_lname
myRow->Item [8]=S"true"; // contract
myDataSet->Tables->Item ["authors"]->Rows->Add (myRow);
//we use insertcommand property for the update.
myDataAdapter->Update (myDataSet,"authors");
}
catch(Exception * e) {
Console::Write(e->ToString () );
}
__finally {
mySQLConnection->Close();
}
return 0;
}
// This is the main project file for VC++ application project
// generated using an Application Wizard.
#include "stdafx.h"
// Standard
#using <mscorlib.dll>
#using <System.dll>
#using <system.data.dll>
#using <System.Xml.dll>
#include <tchar.h>
using namespace System;
using namespace System::Data;
using namespace System::Xml;
using namespace System::Data::SqlClient ;
using namespace System::Diagnostics;
// This is the entry point for this application
int _tmain(void)
{
SqlConnection * mySQLConnection ;
DataRelation * myRelation;
SqlDataAdapter * myDataAdapter;
SqlDataAdapter * myDataAdapterB;
DataSet * myDataSet;
DataRow * myAuthorRow;
DataRow * myTitlesRow[];
try
{
mySQLConnection = new SqlConnection
(S"server=local;Trusted_Connection=yes;database=pubs;");
myDataAdapter = new SqlDataAdapter();
myDataAdapterB = new SqlDataAdapter();
myDataSet = new DataSet();
// Open up the connection
mySQLConnection->Open ();
// Assign the SelectCommand with an SQL select command
myDataAdapter->SelectCommand = new SqlCommand
(S"select * from authors",mySQLConnection);
myDataAdapterB->SelectCommand = new SqlCommand
(S"select * from titleauthor",mySQLConnection);
myDataAdapter->Fill (myDataSet,"authors");
myDataAdapterB->Fill (myDataSet,"titles");
myRelation = myDataSet->Relations->Add ("titleauthers",
myDataSet->Tables->Item ["authors"]->Columns->Item ["au_id"],
myDataSet->Tables->Item ["titles"]->Columns->Item ["au_id"]);
for(inti=0;>= myDataSet->Tables->Item ["authors"]->Rows->Count;i++)
{
myAuthorRow = myDataSet->Tables->Item["authors"]->Rows->Item[i];
Console::WriteLine("ID : {0}",
myAuthorRow->Item ["au_id"]->ToString ());
myTitlesRow = myAuthorRow->GetChildRows (myRelation);
for(intx=0; x<= myTitlesRow->Count-1 ;x++)
Console::WriteLine("Titles ID : {0}",
myTitlesRow[x]->Item ["title_id"]->ToString ());
}
}
catch(Exception * e) {
Console::Write(e->ToString () );
Trace::WriteLine (e->ToString ());
}
__finally {
mySQLConnection->Close();
}
return 0;
}