ADO.NET est l’API .NET Framework pour l’accès aux données et fournit de la puissance et de la facilité d’utilisation sans correspondance par les solutions d’accès aux données précédentes. Cette section décrit certains des problèmes impliquant des ADO.NET propres aux utilisateurs Visual C++, tels que le marshaling de types natifs.
ADO.NET s’exécute sous Common Language Runtime (CLR). Par conséquent, toute application qui interagit avec ADO.NET doit également cibler le CLR. Toutefois, cela ne signifie pas que les applications natives ne peuvent pas utiliser ADO.NET. Ces exemples montrent comment interagir avec une base de données ADO.NET à partir du code natif.
Marshaler des chaînes ANSI pour ADO.NET
Montre comment ajouter une chaîne native (char *) à une base de données et comment marshaler une System.String base de données vers une chaîne native.
Exemple
Dans cet exemple, la classe DatabaseClass est créée pour interagir avec un objet ADO.NET DataTable . Notez que cette classe est un C++ class natif (par rapport à un ref class ou value class). Cela est nécessaire, car nous voulons utiliser cette classe à partir du code natif et vous ne pouvez pas utiliser de types managés dans du code natif. Cette classe sera compilée pour cibler le CLR, comme indiqué par la #pragma managed directive précédant la déclaration de classe. Pour plus d’informations sur cette directive, consultez managed, unmanaged.
Notez le membre privé de la classe DatabaseClass : gcroot<DataTable ^> table. Étant donné que les types natifs ne peuvent pas contenir de types managés, le gcroot mot clé est nécessaire. Pour plus d’informations sur gcroot, consultez Comment : déclarer des handles dans des types natifs.
Le reste du code de cet exemple est du code C++ natif, comme indiqué par la #pragma unmanaged directive précédente main. Dans cet exemple, nous créons une nouvelle instance de DatabaseClass et nous appelons ses méthodes pour créer une table et remplir certaines lignes de la table. Notez que les chaînes C++ natives sont passées en tant que valeurs pour la colonne de base de données StringCol. Dans DatabaseClass, ces chaînes sont marshalées en chaînes managées à l’aide de la fonctionnalité de marshaling trouvée dans l’espace System.Runtime.InteropServices de noms. Plus précisément, la méthode PtrToStringAnsi est utilisée pour marshaler un char * à un String, et la méthode StringToHGlobalAnsi est utilisée pour marshaler un String à un char *.
// adonet_marshal_string_native.cpp// compile with: /clr /FU System.dll /FU System.Data.dll /FU System.Xml.dll#include<comdef.h>#include<gcroot.h>#include<iostream>usingnamespacestd;
#using <System.Data.dll>usingnamespace System;
usingnamespace System::Data;
usingnamespace System::Runtime::InteropServices;
#define MAXCOLS 100#pragma managedclassDatabaseClass
{public:
DatabaseClass() : table(nullptr) { }
voidAddRow(char *stringColValue){
// Add a row to the table.
DataRow ^row = table->NewRow();
row["StringCol"] = Marshal::PtrToStringAnsi(
(IntPtr)stringColValue);
table->Rows->Add(row);
}
voidCreateAndPopulateTable(){
// Create a simple DataTable.
table = gcnew DataTable("SampleTable");
// Add a column of type String to the table.
DataColumn ^column1 = gcnew DataColumn("StringCol",
Type::GetType("System.String"));
table->Columns->Add(column1);
}
intGetValuesForColumn(char *dataColumn, char **values,
int valuesLength){
// Marshal the name of the column to a managed// String.
String ^columnStr = Marshal::PtrToStringAnsi(
(IntPtr)dataColumn);
// Get all rows in the table.array<DataRow ^> ^rows = table->Select();
int len = rows->Length;
len = (len > valuesLength) ? valuesLength : len;
for (int i = 0; i < len; i++)
{
// Marshal each column value from a managed string// to a char *.
values[i] = (char *)Marshal::StringToHGlobalAnsi(
(String ^)rows[i][columnStr]).ToPointer();
}
return len;
}
private:
// Using gcroot, you can use a managed type in// a native class.
gcroot<DataTable ^> table;
};
#pragma unmanagedintmain(){
// Create a table and add a few rows to it.
DatabaseClass *db = new DatabaseClass();
db->CreateAndPopulateTable();
db->AddRow("This is string 1.");
db->AddRow("This is string 2.");
// Now retrieve the rows and display their contents.char *values[MAXCOLS];
int len = db->GetValuesForColumn(
"StringCol", values, MAXCOLS);
for (int i = 0; i < len; i++)
{
cout << "StringCol: " << values[i] << endl;
// Deallocate the memory allocated using// Marshal::StringToHGlobalAnsi.
GlobalFree(values[i]);
}
delete db;
return0;
}
Output
StringCol: This is string 1.
StringCol: This is string 2.
Compilation du code
Pour compiler le code à partir de la ligne de commande, enregistrez l’exemple de code dans un fichier nommé adonet_marshal_string_native.cpp et entrez l’instruction suivante :
Montre comment ajouter une chaîne COM (BSTR) à une base de données et comment marshaler un System.String d’une base de données à un BSTR.
Exemple
Dans cet exemple, la classe DatabaseClass est créée pour interagir avec un objet ADO.NET DataTable . Notez que cette classe est un C++ class natif (par rapport à un ref class ou value class). Cela est nécessaire, car nous voulons utiliser cette classe à partir du code natif et vous ne pouvez pas utiliser de types managés dans du code natif. Cette classe sera compilée pour cibler le CLR, comme indiqué par la #pragma managed directive précédant la déclaration de classe. Pour plus d’informations sur cette directive, consultez managed, unmanaged.
Notez le membre privé de la classe DatabaseClass : gcroot<DataTable ^> table. Étant donné que les types natifs ne peuvent pas contenir de types managés, le gcroot mot clé est nécessaire. Pour plus d’informations sur gcroot, consultez Comment : déclarer des handles dans des types natifs.
Le reste du code de cet exemple est du code C++ natif, comme indiqué par la #pragma unmanaged directive précédente main. Dans cet exemple, nous créons une nouvelle instance de DatabaseClass et nous appelons ses méthodes pour créer une table et remplir certaines lignes de la table. Notez que les chaînes COM sont passées en tant que valeurs pour la colonne de base de données StringCol. Dans DatabaseClass, ces chaînes sont marshalées en chaînes managées à l’aide de la fonctionnalité de marshaling trouvée dans l’espace System.Runtime.InteropServices de noms. Plus précisément, la méthode PtrToStringBSTR est utilisée pour marshaler un BSTR à un String, et la méthode StringToBSTR est utilisée pour marshaler un String à un BSTR.
Notes
La mémoire allouée par StringToBSTR doit être libérée en appelant ou FreeBSTRSysFreeString.
C++
// adonet_marshal_string_bstr.cpp// compile with: /clr /FU System.dll /FU System.Data.dll /FU System.Xml.dll#include<comdef.h>#include<gcroot.h>#include<iostream>usingnamespacestd;
#using <System.Data.dll>usingnamespace System;
usingnamespace System::Data;
usingnamespace System::Runtime::InteropServices;
#define MAXCOLS 100#pragma managedclassDatabaseClass
{public:
DatabaseClass() : table(nullptr) { }
voidAddRow(BSTR stringColValue){
// Add a row to the table.
DataRow ^row = table->NewRow();
row["StringCol"] = Marshal::PtrToStringBSTR(
(IntPtr)stringColValue);
table->Rows->Add(row);
}
voidCreateAndPopulateTable(){
// Create a simple DataTable.
table = gcnew DataTable("SampleTable");
// Add a column of type String to the table.
DataColumn ^column1 = gcnew DataColumn("StringCol",
Type::GetType("System.String"));
table->Columns->Add(column1);
}
intGetValuesForColumn(BSTR dataColumn, BSTR *values,
int valuesLength){
// Marshal the name of the column to a managed// String.
String ^columnStr = Marshal::PtrToStringBSTR(
(IntPtr)dataColumn);
// Get all rows in the table.array<DataRow ^> ^rows = table->Select();
int len = rows->Length;
len = (len > valuesLength) ? valuesLength : len;
for (int i = 0; i < len; i++)
{
// Marshal each column value from a managed string// to a BSTR.
values[i] = (BSTR)Marshal::StringToBSTR(
(String ^)rows[i][columnStr]).ToPointer();
}
return len;
}
private:
// Using gcroot, you can use a managed type in// a native class.
gcroot<DataTable ^> table;
};
#pragma unmanagedintmain(){
// Create a table and add a few rows to it.
DatabaseClass *db = new DatabaseClass();
db->CreateAndPopulateTable();
BSTR str1 = SysAllocString(L"This is string 1.");
db->AddRow(str1);
BSTR str2 = SysAllocString(L"This is string 2.");
db->AddRow(str2);
// Now retrieve the rows and display their contents.
BSTR values[MAXCOLS];
BSTR str3 = SysAllocString(L"StringCol");
int len = db->GetValuesForColumn(
str3, values, MAXCOLS);
for (int i = 0; i < len; i++)
{
wcout << "StringCol: " << values[i] << endl;
// Deallocate the memory allocated using// Marshal::StringToBSTR.
SysFreeString(values[i]);
}
SysFreeString(str1);
SysFreeString(str2);
SysFreeString(str3);
delete db;
return0;
}
Output
StringCol: This is string 1.
StringCol: This is string 2.
Compilation du code
Pour compiler le code à partir de la ligne de commande, enregistrez l’exemple de code dans un fichier nommé adonet_marshal_string_native.cpp et entrez l’instruction suivante :
Montre comment ajouter une chaîne Unicode native (wchar_t *) à une base de données et comment marshaler une System.String base de données à une chaîne Unicode native.
Exemple
Dans cet exemple, la classe DatabaseClass est créée pour interagir avec un objet ADO.NET DataTable . Notez que cette classe est un C++ class natif (par rapport à un ref class ou value class). Cela est nécessaire, car nous voulons utiliser cette classe à partir du code natif et vous ne pouvez pas utiliser de types managés dans du code natif. Cette classe sera compilée pour cibler le CLR, comme indiqué par la #pragma managed directive précédant la déclaration de classe. Pour plus d’informations sur cette directive, consultez managed, unmanaged.
Notez le membre privé de la classe DatabaseClass : gcroot<DataTable ^> table. Étant donné que les types natifs ne peuvent pas contenir de types managés, le gcroot mot clé est nécessaire. Pour plus d’informations sur gcroot, consultez Comment : déclarer des handles dans des types natifs.
Le reste du code de cet exemple est du code C++ natif, comme indiqué par la #pragma unmanaged directive précédente main. Dans cet exemple, nous créons une nouvelle instance de DatabaseClass et nous appelons ses méthodes pour créer une table et remplir certaines lignes de la table. Notez que les chaînes Unicode C++ sont passées en tant que valeurs pour la colonne de base de données StringCol. Dans DatabaseClass, ces chaînes sont marshalées en chaînes managées à l’aide de la fonctionnalité de marshaling trouvée dans l’espace System.Runtime.InteropServices de noms. Plus précisément, la méthode PtrToStringUni est utilisée pour marshaler un wchar_t * à un String, et la méthode StringToHGlobalUni est utilisée pour marshaler un String à un wchar_t *.
// adonet_marshal_string_wide.cpp// compile with: /clr /FU System.dll /FU System.Data.dll /FU System.Xml.dll#include<comdef.h>#include<gcroot.h>#include<iostream>usingnamespacestd;
#using <System.Data.dll>usingnamespace System;
usingnamespace System::Data;
usingnamespace System::Runtime::InteropServices;
#define MAXCOLS 100#pragma managedclassDatabaseClass
{public:
DatabaseClass() : table(nullptr) { }
voidAddRow(wchar_t *stringColValue){
// Add a row to the table.
DataRow ^row = table->NewRow();
row["StringCol"] = Marshal::PtrToStringUni(
(IntPtr)stringColValue);
table->Rows->Add(row);
}
voidCreateAndPopulateTable(){
// Create a simple DataTable.
table = gcnew DataTable("SampleTable");
// Add a column of type String to the table.
DataColumn ^column1 = gcnew DataColumn("StringCol",
Type::GetType("System.String"));
table->Columns->Add(column1);
}
intGetValuesForColumn(wchar_t *dataColumn, wchar_t **values,
int valuesLength){
// Marshal the name of the column to a managed// String.
String ^columnStr = Marshal::PtrToStringUni(
(IntPtr)dataColumn);
// Get all rows in the table.array<DataRow ^> ^rows = table->Select();
int len = rows->Length;
len = (len > valuesLength) ? valuesLength : len;
for (int i = 0; i < len; i++)
{
// Marshal each column value from a managed string// to a wchar_t *.
values[i] = (wchar_t *)Marshal::StringToHGlobalUni(
(String ^)rows[i][columnStr]).ToPointer();
}
return len;
}
private:
// Using gcroot, you can use a managed type in// a native class.
gcroot<DataTable ^> table;
};
#pragma unmanagedintmain(){
// Create a table and add a few rows to it.
DatabaseClass *db = new DatabaseClass();
db->CreateAndPopulateTable();
db->AddRow(L"This is string 1.");
db->AddRow(L"This is string 2.");
// Now retrieve the rows and display their contents.wchar_t *values[MAXCOLS];
int len = db->GetValuesForColumn(
L"StringCol", values, MAXCOLS);
for (int i = 0; i < len; i++)
{
wcout << "StringCol: " << values[i] << endl;
// Deallocate the memory allocated using// Marshal::StringToHGlobalUni.
GlobalFree(values[i]);
}
delete db;
return0;
}
Output
StringCol: This is string 1.
StringCol: This is string 2.
Compilation du code
Pour compiler le code à partir de la ligne de commande, enregistrez l’exemple de code dans un fichier nommé adonet_marshal_string_wide.cpp et entrez l’instruction suivante :
Montre comment ajouter un natif VARIANT à une base de données et comment marshaler une System.Object base de données d’une base de données vers une base de données native VARIANT.
Exemple
Dans cet exemple, la classe DatabaseClass est créée pour interagir avec un objet ADO.NET DataTable . Notez que cette classe est un C++ class natif (par rapport à un ref class ou value class). Cela est nécessaire, car nous voulons utiliser cette classe à partir du code natif et vous ne pouvez pas utiliser de types managés dans du code natif. Cette classe sera compilée pour cibler le CLR, comme indiqué par la #pragma managed directive précédant la déclaration de classe. Pour plus d’informations sur cette directive, consultez managed, unmanaged.
Notez le membre privé de la classe DatabaseClass : gcroot<DataTable ^> table. Étant donné que les types natifs ne peuvent pas contenir de types managés, le gcroot mot clé est nécessaire. Pour plus d’informations sur gcroot, consultez Comment : déclarer des handles dans des types natifs.
Le reste du code de cet exemple est du code C++ natif, comme indiqué par la #pragma unmanaged directive précédente main. Dans cet exemple, nous créons une nouvelle instance de DatabaseClass et nous appelons ses méthodes pour créer une table et remplir certaines lignes de la table. Notez que les types natifs VARIANT sont passés en tant que valeurs pour la colonne de base de données ObjectCol. Dans DatabaseClass, ces VARIANT types sont marshalés en objets managés à l’aide de la fonctionnalité de marshaling trouvée dans l’espace System.Runtime.InteropServices de noms. Plus précisément, la méthode GetObjectForNativeVariant est utilisée pour marshaler un VARIANT à un Object, et la méthode GetNativeVariantForObject est utilisée pour marshaler un Object à un VARIANT.
C++
// adonet_marshal_variant.cpp// compile with: /clr /FU System.dll /FU System.Data.dll /FU System.Xml.dll#include<comdef.h>#include<gcroot.h>#include<iostream>usingnamespacestd;
#using <System.Data.dll>usingnamespace System;
usingnamespace System::Data;
usingnamespace System::Runtime::InteropServices;
#define MAXCOLS 100#pragma managedclassDatabaseClass
{public:
DatabaseClass() : table(nullptr) { }
voidAddRow(VARIANT *objectColValue){
// Add a row to the table.
DataRow ^row = table->NewRow();
row["ObjectCol"] = Marshal::GetObjectForNativeVariant(
IntPtr(objectColValue));
table->Rows->Add(row);
}
voidCreateAndPopulateTable(){
// Create a simple DataTable.
table = gcnew DataTable("SampleTable");
// Add a column of type String to the table.
DataColumn ^column1 = gcnew DataColumn("ObjectCol",
Type::GetType("System.Object"));
table->Columns->Add(column1);
}
intGetValuesForColumn(wchar_t *dataColumn, VARIANT *values,
int valuesLength){
// Marshal the name of the column to a managed// String.
String ^columnStr = Marshal::PtrToStringUni(
(IntPtr)dataColumn);
// Get all rows in the table.array<DataRow ^> ^rows = table->Select();
int len = rows->Length;
len = (len > valuesLength) ? valuesLength : len;
for (int i = 0; i < len; i++)
{
// Marshal each column value from a managed object// to a VARIANT.
Marshal::GetNativeVariantForObject(
rows[i][columnStr], IntPtr(&values[i]));
}
return len;
}
private:
// Using gcroot, you can use a managed type in// a native class.
gcroot<DataTable ^> table;
};
#pragma unmanagedintmain(){
// Create a table and add a few rows to it.
DatabaseClass *db = new DatabaseClass();
db->CreateAndPopulateTable();
BSTR bstr1 = SysAllocString(L"This is a BSTR in a VARIANT.");
VARIANT v1;
v1.vt = VT_BSTR;
v1.bstrVal = bstr1;
db->AddRow(&v1);
int i = 42;
VARIANT v2;
v2.vt = VT_I4;
v2.lVal = i;
db->AddRow(&v2);
// Now retrieve the rows and display their contents.
VARIANT values[MAXCOLS];
int len = db->GetValuesForColumn(
L"ObjectCol", values, MAXCOLS);
for (int i = 0; i < len; i++)
{
switch (values[i].vt)
{
case VT_BSTR:
wcout << L"ObjectCol: " << values[i].bstrVal << endl;
break;
case VT_I4:
cout << "ObjectCol: " << values[i].lVal << endl;
break;
default:
break;
}
}
SysFreeString(bstr1);
delete db;
return0;
}
Output
ObjectCol: This is a BSTR in a VARIANT.
ObjectCol: 42
Compilation du code
Pour compiler le code à partir de la ligne de commande, enregistrez l’exemple de code dans un fichier nommé adonet_marshal_variant.cpp et entrez l’instruction suivante :
Montre comment ajouter un natif SAFEARRAY à une base de données et comment marshaler un tableau managé d’une base de données vers une base de données native SAFEARRAY.
Exemple
Dans cet exemple, la classe DatabaseClass est créée pour interagir avec un objet ADO.NET DataTable . Notez que cette classe est un C++ class natif (par rapport à un ref class ou value class). Cela est nécessaire, car nous voulons utiliser cette classe à partir du code natif et vous ne pouvez pas utiliser de types managés dans du code natif. Cette classe sera compilée pour cibler le CLR, comme indiqué par la #pragma managed directive précédant la déclaration de classe. Pour plus d’informations sur cette directive, consultez managed, unmanaged.
Notez le membre privé de la classe DatabaseClass : gcroot<DataTable ^> table. Étant donné que les types natifs ne peuvent pas contenir de types managés, le gcroot mot clé est nécessaire. Pour plus d’informations sur gcroot, consultez Comment : déclarer des handles dans des types natifs.
Le reste du code de cet exemple est du code C++ natif, comme indiqué par la #pragma unmanaged directive précédente main. Dans cet exemple, nous créons une nouvelle instance de DatabaseClass et nous appelons ses méthodes pour créer une table et remplir certaines lignes de la table. Notez que les types natifs SAFEARRAY sont passés en tant que valeurs pour la colonne de base de données ArrayIntsCol. Dans DatabaseClass, ces SAFEARRAY types sont marshalés en objets managés à l’aide de la fonctionnalité de marshaling trouvée dans l’espace System.Runtime.InteropServices de noms. Plus précisément, la méthode Copy est utilisée pour marshaler un SAFEARRAY tableau managé d’entiers, et la méthode Copy est utilisée pour marshaler un tableau managé d’entiers à un SAFEARRAY.
C++
// adonet_marshal_safearray.cpp// compile with: /clr /FU System.dll /FU System.Data.dll /FU System.Xml.dll#include<comdef.h>#include<gcroot.h>#include<iostream>usingnamespacestd;
#using <System.Data.dll>usingnamespace System;
usingnamespace System::Data;
usingnamespace System::Runtime::InteropServices;
#define MAXCOLS 100#pragma managedclassDatabaseClass
{public:
DatabaseClass() : table(nullptr) { }
voidAddRow(SAFEARRAY *arrayIntsColValue){
// Add a row to the table.
DataRow ^row = table->NewRow();
int len = arrayIntsColValue->rgsabound[0].cElements;
array<int> ^arr = gcnew array<int>(len);
int *pData;
SafeArrayAccessData(arrayIntsColValue, (void **)&pData);
Marshal::Copy(IntPtr(pData), arr, 0, len);
SafeArrayUnaccessData(arrayIntsColValue);
row["ArrayIntsCol"] = arr;
table->Rows->Add(row);
}
voidCreateAndPopulateTable(){
// Create a simple DataTable.
table = gcnew DataTable("SampleTable");
// Add a column of type String to the table.
DataColumn ^column1 = gcnew DataColumn("ArrayIntsCol",
Type::GetType("System.Int32[]"));
table->Columns->Add(column1);
}
intGetValuesForColumn(wchar_t *dataColumn, SAFEARRAY **values,
int valuesLength){
// Marshal the name of the column to a managed// String.
String ^columnStr = Marshal::PtrToStringUni(
(IntPtr)dataColumn);
// Get all rows in the table.array<DataRow ^> ^rows = table->Select();
int len = rows->Length;
len = (len > valuesLength) ? valuesLength : len;
for (int i = 0; i < len; i++)
{
// Marshal each column value from a managed array// of Int32s to a SAFEARRAY of type VT_I4.
values[i] = SafeArrayCreateVector(VT_I4, 0, 10);
int *pData;
SafeArrayAccessData(values[i], (void **)&pData);
Marshal::Copy((array<int> ^)rows[i][columnStr], 0,
IntPtr(pData), 10);
SafeArrayUnaccessData(values[i]);
}
return len;
}
private:
// Using gcroot, you can use a managed type in// a native class.
gcroot<DataTable ^> table;
};
#pragma unmanagedintmain(){
// Create a table and add a few rows to it.
DatabaseClass *db = new DatabaseClass();
db->CreateAndPopulateTable();
// Create a standard array.int originalArray[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// Create a SAFEARRAY.
SAFEARRAY *psa;
psa = SafeArrayCreateVector(VT_I4, 0, 10);
// Copy the data from the original array to the SAFEARRAY.int *pData;
HRESULT hr = SafeArrayAccessData(psa, (void **)&pData);
memcpy(pData, &originalArray, 40);
SafeArrayUnaccessData(psa);
db->AddRow(psa);
// Now retrieve the rows and display their contents.
SAFEARRAY *values[MAXCOLS];
int len = db->GetValuesForColumn(
L"ArrayIntsCol", values, MAXCOLS);
for (int i = 0; i < len; i++)
{
int *pData;
SafeArrayAccessData(values[i], (void **)&pData);
for (int j = 0; j < 10; j++)
{
cout << pData[j] << " ";
}
cout << endl;
SafeArrayUnaccessData(values[i]);
// Deallocate the memory allocated using// SafeArrayCreateVector.
SafeArrayDestroy(values[i]);
}
SafeArrayDestroy(psa);
delete db;
return0;
}
Output
0 1 2 3 4 5 6 7 8 9
Compilation du code
Pour compiler le code à partir de la ligne de commande, enregistrez l’exemple de code dans un fichier nommé adonet_marshal_safearray.cpp et entrez l’instruction suivante :
Découvrez les systèmes de base de données auxquels .NET Aspire peut se connecter à l’aide de composants intégrés. Découvrez ensuite comment configurer des connexions et stocker des données dans des bases de données relationnelles et non relationnelles.