Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Ez a cikk a Microsoft.Data.SqlClient használatával történő aszinkron programozást ismerteti. Telepítse a NuGet-csomagot, és importálja a(z) Microsoft.Data.SqlClient névteret az SQL-példákhoz. A Microsoft.Data.SqlClient támogatja a feladatalapú API-kat a .NET-keretrendszeren és a modern .NET-en.
Hagyományos aszinkron programozás
A .NET Framework 4.5 előtt aszinkron programozás történt a System.Data.SqlClient használatával a következő módszerekkel és a Asynchronous Processing=true kapcsolati tulajdonsággal:
Ez a funkció az SqlClientben maradt a .NET-keretrendszer 4.5-ben.
Tipp.
A Microsoft.Data.SqlClient a kompatibilitás érdekében a BeginExecuteNonQuery, BeginExecuteReader és BeginExecuteXmlReader elemeket is biztosítja. Távolítsa el a(z) Asynchronous Processing elemet a kapcsolati sztringekből a Microsoft.Data.SqlClient szolgáltatóra való migráláskor. A(z) true vagy false használatakor a kulcsszó ArgumentException eredményez a(z) Keyword not supported: 'asynchronous processing'. üzenettel, amikor létrehozza a(z) SqlConnection objektumot, vagy értéket ad a(z) ConnectionString tulajdonságának, mielőtt meghívná a(z) OpenAsync vagy Open elemet. A kulcsszót nem hagyja figyelmen kívül, és nem kötelező az aszinkron hívásokhoz.
A .NET-keretrendszer 4.5-ben hozzáadott aszinkron programozási funkciók
Az aszinkron programozási funkció egyszerű technikát biztosít a kód aszinkronvá alakításához. A .NET-keretrendszer 4.5-ben bevezetett aszinkron programozási funkcióval kapcsolatos további információkért lásd:
- Aszinkron programozás C-ben#
- Aszinkron programozás Async és Await használatával (Visual Basic)
- Az SqlDataReader új aszinkron metódusainak használata a .NET-keretrendszer 4.5-ben (1. rész)
- Az SqlDataReader új aszinkron metódusainak használata a .NET-keretrendszer 4.5-ben (2. rész)
Ha a felhasználói felület nem válaszol, vagy a kiszolgáló nem méretez, valószínű, hogy a kódnak aszinkronabbnak kell lennie. Az aszinkron kód írása hagyományosan egy visszahívás (más néven folytatás) telepítésével fejezte ki az aszinkron művelet befejeződése után felmerülő logikát. Ez bonyolítja az aszinkron kód szerkezetét a szinkron kóddal összehasonlítva.
Mostantól visszahívások nélkül is meghívhat aszinkron metódusokat, anélkül, hogy felosztaná a kódját több metódusra vagy lambdakifejezésre.
A async módosító megadja, hogy egy metódus aszinkron. Metódus meghívásakor a async egy feladatot ad vissza. Amikor az await operátort egy tevékenységre alkalmazza, az aktuális metódus azonnal kilép. Amikor a tevékenység befejeződik, a végrehajtás ugyanabban a metódusban folytatódik.
Megjegyzés
A Microsoft.Data.SqlClient nem támogatja a Context Connection=true. Amikor ezzel a beállítással hozza létre a SqlConnection objektumot, vagy beállítja a ConnectionString tulajdonságát, az illesztőprogram Connecting to the context connection using Microsoft.Data.SqlClient is not supported. kivételt vált ki a következő üzenettel: InvalidOperationException. A kivétel még a Open vagy a OpenAsync meghívása előtt bekövetkezik; a beállítás nem marad figyelmen kívül.
Egy async metódus meghívása nem foglal le további szálakat. Előfordulhat, hogy a meglévő I/O-befejezési szálat rövid időre használja a végén.
Az alábbi bázisosztály- és Microsoft.Data.SqlClient-metódusok támogatják az aszinkron programozást:
- DbConnection.OpenAsync
- DbCommand.ExecuteDbDataReaderAsync
- DbCommand.ExecuteNonQueryAsync
- DbCommand.ExecuteReaderAsync
- DbCommand.ExecuteScalarAsync
- GetFieldValueAsync
- IsDBNullAsync
- DbDataReader.NextResultAsync
- DbDataReader.ReadAsync
- SqlConnection.OpenAsync
- SqlCommand.ExecuteNonQueryAsync
- SqlCommand.ExecuteReaderAsync
- SqlCommand.ExecuteScalarAsync
- SqlCommand.ExecuteXmlReaderAsync
- SqlDataReader.NextResultAsync
- SqlDataReader.ReadAsync
- SqlBulkCopy.WriteToServerAsync
További aszinkron tagok lettek hozzáadva az SqlClient streaming támogatásához.
Tipp.
A feladatalapú aszinkron metódusok nem igényelnek kapcsolati karakterlánc beállítást az aszinkron műveletek engedélyezéséhez.
Szinkronról aszinkronra váltó kapcsolat megnyitása
Frissíthet egy régebbi alkalmazást az újabb aszinkron funkció használatára. Tegyük fel például, hogy egy alkalmazás szinkron kapcsolati algoritmussal rendelkezik, és blokkolja a felhasználói felületi szálat minden alkalommal, amikor az adatbázishoz csatlakozik, és a csatlakozás után az alkalmazás egy tárolt eljárást hív meg, amely az imént bejelentkezett felhasználót jelzi.
using SqlConnection conn = new SqlConnection("...");
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("StoredProcedure_Logon", conn))
{
cmd.ExecuteNonQuery();
}
}
Az újabb aszinkron funkció használatára konvertált program a következőképpen nézne ki:
using System;
using Microsoft.Data.SqlClient;
using System.Threading.Tasks;
class A {
static async Task<int> Method(SqlConnection conn, SqlCommand cmd) {
await conn.OpenAsync();
await cmd.ExecuteNonQueryAsync();
return 1;
}
public static void Main() {
using (SqlConnection conn = new SqlConnection("...")) {
SqlCommand command = new SqlCommand("select top 2 * from orders", conn);
int result = A.Method(conn, command).Result;
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
Console.WriteLine(reader[0]);
}
}
}
Adja hozzá az új aszinkron funkciót egy meglévő alkalmazásban (a régi és az új mintákat keverje)
Új aszinkron képességet (SqlConnection::OpenAsync) is hozzáadhat a meglévő aszinkron logika módosítása nélkül. Ha például egy alkalmazás jelenleg a következőt használja:
AsyncCallback productList = new AsyncCallback(ProductList);
SqlConnection conn = new SqlConnection("...");
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM [Current Product List]", conn);
IAsyncResult ia = cmd.BeginExecuteReader(productList, cmd);
Az új aszinkron mintát a meglévő algoritmus lényeges módosítása nélkül is használhatja.
Az alábbi sablon szándékosan kihagyja a ProductList callback törzsét. A visszahívásban hívja meg EndExecuteReader-t, dolgozza fel és dobja el az olvasóobjektumot, és dobja el a parancsot és a kapcsolatot. Tartsa életben az alkalmazást, amíg a visszahívási függvény be nem fejeződik. A sablon nem önálló konzolalkalmazás.
using System;
using Microsoft.Data.SqlClient;
using System.Threading.Tasks;
class A {
static void ProductList(IAsyncResult result) { }
public static void Main()
{
AsyncCallback productList = new AsyncCallback(ProductList);
SqlConnection conn = new SqlConnection("...");
conn.OpenAsync().ContinueWith((task) => {
SqlCommand cmd = new SqlCommand("select top 2 * from orders", conn);
IAsyncResult ia = cmd.BeginExecuteReader(productList, cmd);
}, TaskContinuationOptions.OnlyOnRanToCompletion);
}
}
Az alapszolgáltatói modell és az új aszinkron funkció használata
Előfordulhat, hogy létre kell hoznia egy eszközt, amely képes csatlakozni a különböző adatbázisokhoz, és lekérdezéseket végrehajtani. Használhatja az alapszolgáltatói modellt és az új aszinkron funkciót.
A Microsoft Elosztott tranzakcióvezérlőt (MSDTC) engedélyezni kell a kiszolgálón az elosztott tranzakciók használatához. Az MSDTC engedélyezéséről további információt az MSDTC engedélyezése webkiszolgálón című témakörben talál.
A Factory példa futtatása előtt regisztráljon Microsoft.Data.SqlClient a DbProviderFactory beszerzése című cikkben leírtak szerint. Használja az alkalmazáskonfigurációt a .NET-keretrendszerben vagy DbProviderFactories.RegisterFactory a modern .NET-ben.
using System;
using System.Data.Common;
using Microsoft.Data.SqlClient;
using System.Threading.Tasks;
class A {
static async Task PerformDBOperationsUsingProviderModel(string connectionString, string providerName) {
DbProviderFactory factory = DbProviderFactories.GetFactory(providerName);
using (DbConnection connection = factory.CreateConnection()) {
connection.ConnectionString = connectionString;
await connection.OpenAsync();
DbCommand command = connection.CreateCommand();
command.CommandText = "SELECT * FROM AUTHORS";
using (DbDataReader reader = await command.ExecuteReaderAsync()) {
while (await reader.ReadAsync()) {
for (int i = 0; i < reader.FieldCount; i++) {
// Process each column as appropriate
object obj = await reader.GetFieldValueAsync<object>(i);
Console.WriteLine(obj);
}
}
}
}
}
public static void Main()
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
// replace these with your own values
builder.DataSource = "your_server";
builder.InitialCatalog = "pubs";
builder.IntegratedSecurity = true;
string provider = "Microsoft.Data.SqlClient";
Task task = PerformDBOperationsUsingProviderModel(builder.ConnectionString, provider);
task.Wait();
}
}
Fontos
A Microsoft azt javasolja, hogy a legbiztonságosabb hitelesítési folyamatot használja. Ha az Azure SQL-hez csatlakozik, az Azure-erőforrásokhoz használt felügyelt identitások az ajánlott hitelesítési módszer.
SQL-tranzakciók és az új aszinkron funkció használata
using System;
using Microsoft.Data.SqlClient;
using System.Threading.Tasks;
class Program {
static void Main() {
string connectionString = "...";
Task task = ExecuteSqlTransaction(connectionString);
task.Wait();
}
static async Task ExecuteSqlTransaction(string connectionString) {
using (SqlConnection connection = new SqlConnection(connectionString)) {
await connection.OpenAsync();
SqlCommand command = connection.CreateCommand();
SqlTransaction transaction = null;
// Start a local transaction.
transaction = await Task.Run<SqlTransaction>(
() => connection.BeginTransaction("SampleTransaction")
);
// Must assign both transaction object and connection
// to Command object for a pending local transaction
command.Connection = connection;
command.Transaction = transaction;
try {
command.CommandText =
"Insert into Region (RegionID, RegionDescription) VALUES (555, 'Description')";
await command.ExecuteNonQueryAsync();
command.CommandText =
"Insert into Region (RegionID, RegionDescription) VALUES (556, 'Description')";
await command.ExecuteNonQueryAsync();
// Attempt to commit the transaction.
await Task.Run(() => transaction.Commit());
Console.WriteLine("Both records are written to database.");
}
catch (Exception ex) {
Console.WriteLine("Commit Exception Type: {0}", ex.GetType());
Console.WriteLine(" Message: {0}", ex.Message);
// Attempt to roll back the transaction.
try {
transaction.Rollback();
}
catch (Exception ex2) {
// This catch block will handle any errors that may have occurred
// on the server that would cause the rollback to fail, such as
// a closed connection.
Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType());
Console.WriteLine(" Message: {0}", ex2.Message);
}
}
}
}
}
SQL-tranzakciók és az új aszinkron funkció használata
Egy vállalati alkalmazásban előfordulhat, hogy elosztott tranzakciókat kell hozzáadnia bizonyos helyzetekben, hogy több adatbázis-kiszolgáló közötti tranzakciókat engedélyezhessen. Használhatja a System.Transactions névteret, és az alábbiak szerint vehet fel elosztott tranzakciót:
A modern .NET-ben az elosztott tranzakciókhoz Windowsra és .NET 7-es vagy újabb verzióra van szükség. Konfigurálja a Microsoft Distributed Transaction Coordinator (MSDTC) hálózati hozzáférését a részt vevő számítógépeken. Mielőtt a következő példát futtatja a modern .NET-ben, engedélyezze a tranzakciók promotálását az alkalmazás indításakor, mielőtt bármilyen tranzakciót létrehozna:
TransactionManager.ImplicitDistributedTransactions = true;
A ImplicitDistributedTransactions tulajdonság alapértelmezett értéke false. E beleegyezés nélkül egy elosztott tranzakcióra való előléptetés NotSupportedException kivételt dob. A .NET-keretrendszerben hagyja ki ezt a hozzárendelést, mert a tulajdonság nem érhető el; Az MSDTC konfigurálása továbbra is szükséges.
using System;
using Microsoft.Data.SqlClient;
using System.Threading.Tasks;
using System.Transactions;
class Program {
public static void Main()
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
// replace these with your own values
builder.DataSource = "your_server";
builder.InitialCatalog = "your_data_source";
builder.IntegratedSecurity = true;
Task task = ExecuteDistributedTransaction(builder.ConnectionString, builder.ConnectionString);
task.Wait();
}
static async Task ExecuteDistributedTransaction(string connectionString1, string connectionString2) {
using (SqlConnection connection1 = new SqlConnection(connectionString1))
using (SqlConnection connection2 = new SqlConnection(connectionString2)) {
using (CommittableTransaction transaction = new CommittableTransaction()) {
await connection1.OpenAsync();
connection1.EnlistTransaction(transaction);
await connection2.OpenAsync();
connection2.EnlistTransaction(transaction);
try {
SqlCommand command1 = connection1.CreateCommand();
command1.CommandText = "Insert into RegionTable1 (RegionID, RegionDescription) VALUES (100, 'Description')";
await command1.ExecuteNonQueryAsync();
SqlCommand command2 = connection2.CreateCommand();
command2.CommandText = "Insert into RegionTable2 (RegionID, RegionDescription) VALUES (100, 'Description')";
await command2.ExecuteNonQueryAsync();
transaction.Commit();
}
catch (Exception ex) {
Console.WriteLine("Exception Type: {0}", ex.GetType());
Console.WriteLine(" Message: {0}", ex.Message);
try {
transaction.Rollback();
}
catch (Exception ex2) {
Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType());
Console.WriteLine(" Message: {0}", ex2.Message);
}
}
}
}
}
}
Fontos
A Microsoft azt javasolja, hogy a legbiztonságosabb hitelesítési folyamatot használja. Ha az Azure SQL-hez csatlakozik, az Azure-erőforrásokhoz használt felügyelt identitások az ajánlott hitelesítési módszer.
Aszinkron művelet megszakítása
Az aszinkron kérés lemondható a CancellationToken használatával.
using System;
using Microsoft.Data.SqlClient;
using System.Threading;
using System.Threading.Tasks;
namespace Samples {
class CancellationSample {
public static void Main(string[] args) {
CancellationTokenSource source = new CancellationTokenSource();
source.CancelAfter(2000); // give up after 2 seconds
try {
Task result = CancellingAsynchronousOperations(source.Token);
result.Wait();
}
catch (AggregateException exception) {
if (source.IsCancellationRequested &&
(exception.InnerException is OperationCanceledException ||
exception.InnerException is SqlException)) {
Console.WriteLine("Operation canceled");
}
else {
throw;
}
}
}
static async Task CancellingAsynchronousOperations(CancellationToken cancellationToken) {
using (SqlConnection connection = new SqlConnection("...")) {
await connection.OpenAsync(cancellationToken);
SqlCommand command = new SqlCommand("WAITFOR DELAY '00:10:00'", connection);
await command.ExecuteNonQueryAsync(cancellationToken);
}
}
}
}
Aszinkron műveletek az SqlBulkCopyval
Használja a SqlBulkCopy.WriteToServerAsync elemet aszinkron tömeges másoláshoz a Microsoft.Data.SqlClient.SqlBulkCopy elemmel.
Használjon külön kapcsolati sztringeket az SqlClienthez és az ODBC-hez. Cserélje le a(z) <server> elemet a saját SQL Server-példányára, és telepítse a SQL Serverhez készült ODBC Driver 18-at a vegyes szolgáltatós példához. Modern .NET esetén hivatkozzon a System.Data.Odbc csomagra is. Az SqlClient kapcsolati karakterlánc több aktív eredményhalmazt (MARS) engedélyez a végső példához.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Odbc;
using Microsoft.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SqlBulkCopyAsyncCodeSample {
class Program {
static string selectStatement = "SELECT * FROM [pubs].[dbo].[titles]";
static string createDestTableStatement =
@"CREATE TABLE {0} (
[title_id] [varchar](6) NOT NULL,
[title] [varchar](80) NOT NULL,
[type] [char](12) NOT NULL,
[pub_id] [char](4) NULL,
[price] [money] NULL,
[advance] [money] NULL,
[royalty] [int] NULL,
[ytd_sales] [int] NULL,
[notes] [varchar](200) NULL,
[pubdate] [datetime] NOT NULL)";
static string connectionString =
"Server=<server>;Database=pubs;Integrated Security=true;Encrypt=true;MultipleActiveResultSets=true;";
static string odbcConnectionString =
"Driver={ODBC Driver 18 for SQL Server};Server=<server>;Database=pubs;Trusted_Connection=yes;Encrypt=yes;";
static void Main(string[] args) {
SynchronousSqlBulkCopy();
AsyncSqlBulkCopy().Wait();
MixSyncAsyncSqlBulkCopy().Wait();
AsyncSqlBulkCopyNotifyAfter().Wait();
AsyncSqlBulkCopyDataRows().Wait();
AsyncSqlBulkCopyMARS().Wait();
}
// 3.1.1 Synchronous bulk copy
private static void SynchronousSqlBulkCopy() {
using (SqlConnection conn = new SqlConnection(connectionString)) {
conn.Open();
DataTable dt = new DataTable();
using (SqlCommand cmd = new SqlCommand(selectStatement, conn)) {
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(dt);
string temptable = "[#" + Guid.NewGuid().ToString("N") + "]";
cmd.CommandText = string.Format(createDestTableStatement, temptable);
cmd.ExecuteNonQuery();
using (SqlBulkCopy bcp = new SqlBulkCopy(conn)) {
bcp.DestinationTableName = temptable;
bcp.WriteToServer(dt);
}
}
}
}
// 3.1.2 Asynchronous bulk copy
private static async Task AsyncSqlBulkCopy() {
using (SqlConnection conn = new SqlConnection(connectionString)) {
await conn.OpenAsync();
DataTable dt = new DataTable();
using (SqlCommand cmd = new SqlCommand(selectStatement, conn)) {
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(dt);
string temptable = "[#" + Guid.NewGuid().ToString("N") + "]";
cmd.CommandText = string.Format(createDestTableStatement, temptable);
await cmd.ExecuteNonQueryAsync();
using (SqlBulkCopy bcp = new SqlBulkCopy(conn)) {
bcp.DestinationTableName = temptable;
await bcp.WriteToServerAsync(dt);
}
}
}
}
// 3.2 Add new Async.NET capabilities in an existing application (Mixing synchronous and asynchronous calls)
private static async Task MixSyncAsyncSqlBulkCopy() {
using (OdbcConnection odbcconn = new OdbcConnection(odbcConnectionString)) {
odbcconn.Open();
using (OdbcCommand odbccmd = new OdbcCommand(selectStatement, odbcconn)) {
using (OdbcDataReader odbcreader = odbccmd.ExecuteReader()) {
using (SqlConnection conn = new SqlConnection(connectionString)) {
await conn.OpenAsync();
string temptable = "[#" + Guid.NewGuid().ToString("N") + "]";
SqlCommand createCmd = new SqlCommand(string.Format(createDestTableStatement, temptable), conn);
await createCmd.ExecuteNonQueryAsync();
using (SqlBulkCopy bcp = new SqlBulkCopy(conn)) {
bcp.DestinationTableName = temptable;
await bcp.WriteToServerAsync(odbcreader);
}
}
}
}
}
}
// 3.3 Using the NotifyAfter property
private static async Task AsyncSqlBulkCopyNotifyAfter() {
using (SqlConnection conn = new SqlConnection(connectionString)) {
await conn.OpenAsync();
DataTable dt = new DataTable();
using (SqlCommand cmd = new SqlCommand(selectStatement, conn)) {
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(dt);
string temptable = "[#" + Guid.NewGuid().ToString("N") + "]";
cmd.CommandText = string.Format(createDestTableStatement, temptable);
await cmd.ExecuteNonQueryAsync();
using (SqlBulkCopy bcp = new SqlBulkCopy(conn)) {
bcp.DestinationTableName = temptable;
bcp.NotifyAfter = 5;
bcp.SqlRowsCopied += new SqlRowsCopiedEventHandler(OnSqlRowsCopied);
await bcp.WriteToServerAsync(dt);
}
}
}
}
private static void OnSqlRowsCopied(object sender, SqlRowsCopiedEventArgs e) {
Console.WriteLine("Copied {0} so far...", e.RowsCopied);
}
// 3.4 Using the new SqlBulkCopy Async.NET capabilities with DataRow[]
private static async Task AsyncSqlBulkCopyDataRows() {
using (SqlConnection conn = new SqlConnection(connectionString)) {
await conn.OpenAsync();
DataTable dt = new DataTable();
using (SqlCommand cmd = new SqlCommand(selectStatement, conn)) {
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(dt);
DataRow[] rows = dt.Select();
string temptable = "[#" + Guid.NewGuid().ToString("N") + "]";
cmd.CommandText = string.Format(createDestTableStatement, temptable);
await cmd.ExecuteNonQueryAsync();
using (SqlBulkCopy bcp = new SqlBulkCopy(conn)) {
bcp.DestinationTableName = temptable;
await bcp.WriteToServerAsync(rows);
}
}
}
}
// 3.7 Using Async.Net and MARS
private static async Task AsyncSqlBulkCopyMARS() {
using (SqlConnection marsConn = new SqlConnection(connectionString)) {
await marsConn.OpenAsync();
SqlCommand titlesCmd = new SqlCommand("SELECT * FROM [pubs].[dbo].[titles]", marsConn);
SqlCommand authorsCmd = new SqlCommand("SELECT * FROM [pubs].[dbo].[authors]", marsConn);
//With MARS we can have multiple active results sets on the same connection
using (SqlDataReader titlesReader = await titlesCmd.ExecuteReaderAsync())
using (SqlDataReader authorsReader = await authorsCmd.ExecuteReaderAsync()) {
await authorsReader.ReadAsync();
string temptable = "[#" + Guid.NewGuid().ToString("N") + "]";
using (SqlConnection destConn = new SqlConnection(connectionString)) {
await destConn.OpenAsync();
using (SqlCommand destCmd = new SqlCommand(string.Format(createDestTableStatement, temptable), destConn)) {
await destCmd.ExecuteNonQueryAsync();
using (SqlBulkCopy bcp = new SqlBulkCopy(destConn)) {
bcp.DestinationTableName = temptable;
await bcp.WriteToServerAsync(titlesReader);
}
}
}
}
}
}
}
}
Aszinkron módon használjon több parancsot a MARS használatával
A példa egyetlen kapcsolatot nyit meg az AdventureWorks adatbázissal. Egy SqlCommand objektum használatával létrejön egy SqlDataReader. Amikor az olvasó használatban van, megnyílik egy második SqlDataReader, amely az elsőből SqlDataReader származó adatokat használja a WHERE záradék bemeneteként a második olvasó számára.
Megjegyzés
Az alábbi példa az SQL Serverhez mellékelt mintaadatbázist AdventureWorks használja. A mintakódban megadott kapcsolati sztring feltételezi, hogy az adatbázis telepítve van, és elérhető a helyi számítógépen. Szükség szerint módosítsa a kapcsolati karakterláncot a környezethez.
using System;
using System.Data;
using Microsoft.Data.SqlClient;
using System.Threading.Tasks;
class Class1 {
static void Main()
{
Task task = MultipleCommands();
task.Wait();
}
static async Task MultipleCommands()
{
// By default, MARS is disabled when connecting to a MARS-enabled.
// It must be enabled in the connection string.
string connectionString = GetConnectionString();
int vendorID;
SqlDataReader productReader = null;
string vendorSQL =
"SELECT VendorId, Name FROM Purchasing.Vendor";
string productSQL =
"SELECT Production.Product.Name FROM Production.Product " +
"INNER JOIN Purchasing.ProductVendor " +
"ON Production.Product.ProductID = " +
"Purchasing.ProductVendor.ProductID " +
"WHERE Purchasing.ProductVendor.VendorID = @VendorId";
using (SqlConnection awConnection =
new SqlConnection(connectionString)) {
SqlCommand vendorCmd = new SqlCommand(vendorSQL, awConnection);
SqlCommand productCmd =
new SqlCommand(productSQL, awConnection);
productCmd.Parameters.Add("@VendorId", SqlDbType.Int);
await awConnection.OpenAsync();
using (SqlDataReader vendorReader = await vendorCmd.ExecuteReaderAsync()) {
while (await vendorReader.ReadAsync()) {
Console.WriteLine(vendorReader["Name"]);
vendorID = (int)vendorReader["VendorId"];
productCmd.Parameters["@VendorId"].Value = vendorID;
// The following line of code requires a MARS-enabled connection.
productReader = await productCmd.ExecuteReaderAsync();
using (productReader) {
while (await productReader.ReadAsync()) {
Console.WriteLine(" " +
productReader["Name"].ToString());
}
}
}
}
}
}
}
Adatok aszinkron olvasása és frissítése a MARS használatával
A MARS lehetővé teszi a kapcsolat használatát olvasási műveletekhez és az adatmanipulációs nyelvi (DML) műveletekhez több függőben lévő művelettel. Ez a funkció szükségtelenné teszi, hogy egy alkalmazás kezelje a kapcsolati foglaltsági hibákat. Emellett a MARS lecserélheti a kiszolgálóoldali kurzorok felhasználóját, amelyek általában több erőforrást használnak fel. Végül, mivel több művelet is működhet egyetlen kapcsolaton, ugyanazzal a tranzakciós környezettel rendelkeznek, így nincs szükség a használatra sp_getbindtoken és sp_bindsession a rendszer által tárolt eljárásokra.
Az alábbi konzolalkalmazás bemutatja, hogyan használhat két SqlDataReader objektumot három SqlCommand objektummal és egyetlen SqlConnection objektummal, amelyeken engedélyezve van a MARS. Az első parancsobjektum lekéri azoknak a szállítóknak a listáját, akiknek a hitelképessége 5. A második parancsobjektum az a SqlDataReader gyártótól kapott szállítói azonosítót használja, hogy betöltse a másodikat SqlDataReader az adott szállító összes termékével. Minden termékrekordot a második SqlDataReader látogat meg. A rendszer számítással határozza meg, hogy mi legyen az új OnOrderQty . Ezután a harmadik parancsobjektummal frissíti a táblát ProductVendor az új értékkel. Ez a teljes folyamat egyetlen tranzakción belül történik, amely a végén lesz visszaállítva.
Megjegyzés
Az alábbi példa az SQL Serverhez mellékelt mintaadatbázist AdventureWorks használja. A mintakódban megadott kapcsolati sztring feltételezi, hogy az adatbázis telepítve van, és elérhető a helyi számítógépen. Szükség szerint módosítsa a kapcsolati karakterláncot a környezethez.
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Microsoft.Data.SqlClient;
using System.Threading.Tasks;
class Program {
static void Main() {
Task task = ReadingAndUpdatingData();
task.Wait();
}
static async Task ReadingAndUpdatingData() {
// By default, MARS is disabled when connecting to a MARS-enabled host.
// It must be enabled in the connection string.
string connectionString = GetConnectionString();
SqlTransaction updateTx = null;
SqlCommand vendorCmd = null;
SqlCommand prodVendCmd = null;
SqlCommand updateCmd = null;
SqlDataReader prodVendReader = null;
int vendorID = 0;
int productID = 0;
int minOrderQty = 0;
int maxOrderQty = 0;
int onOrderQty = 0;
int recordsUpdated = 0;
int totalRecordsUpdated = 0;
string vendorSQL =
"SELECT VendorID, Name FROM Purchasing.Vendor " +
"WHERE CreditRating = 5";
string prodVendSQL =
"SELECT ProductID, MaxOrderQty, MinOrderQty, OnOrderQty " +
"FROM Purchasing.ProductVendor " +
"WHERE VendorID = @VendorID";
string updateSQL =
"UPDATE Purchasing.ProductVendor " +
"SET OnOrderQty = @OrderQty " +
"WHERE ProductID = @ProductID AND VendorID = @VendorID";
using (SqlConnection awConnection =
new SqlConnection(connectionString)) {
await awConnection.OpenAsync();
updateTx = await Task.Run(() => awConnection.BeginTransaction());
vendorCmd = new SqlCommand(vendorSQL, awConnection);
vendorCmd.Transaction = updateTx;
prodVendCmd = new SqlCommand(prodVendSQL, awConnection);
prodVendCmd.Transaction = updateTx;
prodVendCmd.Parameters.Add("@VendorId", SqlDbType.Int);
updateCmd = new SqlCommand(updateSQL, awConnection);
updateCmd.Transaction = updateTx;
updateCmd.Parameters.Add("@OrderQty", SqlDbType.Int);
updateCmd.Parameters.Add("@ProductID", SqlDbType.Int);
updateCmd.Parameters.Add("@VendorID", SqlDbType.Int);
using (SqlDataReader vendorReader = await vendorCmd.ExecuteReaderAsync()) {
while (await vendorReader.ReadAsync()) {
Console.WriteLine(vendorReader["Name"]);
vendorID = (int)vendorReader["VendorID"];
prodVendCmd.Parameters["@VendorID"].Value = vendorID;
prodVendReader = await prodVendCmd.ExecuteReaderAsync();
using (prodVendReader) {
while (await prodVendReader.ReadAsync()) {
productID = (int)prodVendReader["ProductID"];
if (prodVendReader["OnOrderQty"] == DBNull.Value) {
minOrderQty = (int)prodVendReader["MinOrderQty"];
onOrderQty = minOrderQty;
}
else {
maxOrderQty = (int)prodVendReader["MaxOrderQty"];
onOrderQty = (int)(maxOrderQty / 2);
}
updateCmd.Parameters["@OrderQty"].Value = onOrderQty;
updateCmd.Parameters["@ProductID"].Value = productID;
updateCmd.Parameters["@VendorID"].Value = vendorID;
recordsUpdated = await updateCmd.ExecuteNonQueryAsync();
totalRecordsUpdated += recordsUpdated;
}
}
}
}
Console.WriteLine("Total Records Updated: {0}", totalRecordsUpdated);
await Task.Run(() => updateTx.Rollback());
Console.WriteLine("Transaction Rolled Back");
}
}
}