Aszinkron programozás
Ez a témakör az SQL Serverhez készült .NET-keretrendszer adatszolgáltató (SqlClient) aszinkron programozásának támogatását ismerteti, beleértve a .NET-keretrendszer 4.5-ben bevezetett aszinkron programozási funkciókat támogató fejlesztéseket is.
Örökölt aszinkron programozás
A 4.5-ös .NET-keretrendszer előtt az SqlClient aszinkron programozása az alábbi módszerekkel és a Asynchronous Processing=true
kapcsolati tulajdonsággal történt:
Ez a funkció az SqlClientben marad a .NET-keretrendszer 4.5-ben.
Tipp.
A .NET-keretrendszer 4.5-től kezdődően ezek az örökölt módszerek már nem szükségesek Asynchronous Processing=true
a kapcsolati sztring.
Aszinkron programozási funkciók hozzáadva a .NET-keretrendszer 4.5-ben
Az új 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 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 betárcsázhat aszinkron metódusokba, és nem oszthatja fel a kódot több metódusra vagy lambdakifejezésre.
A async
módosító megadja, hogy egy metódus aszinkron. Metódus meghívásakor async
a függvény 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.
Figyelmeztetés
Az aszinkron hívások nem támogatottak, ha egy alkalmazás a Context Connection
kapcsolati sztring kulcsszót is használja.
A metódus meghívása async
nem foglal le további szálakat. A meglévő I/O-befejezési szálat rövid ideig használhatja a végén.
Az aszinkron programozás támogatásához a következő metódusok lettek hozzáadva a .NET-keretrendszer 4.5-ben:
További aszinkron tagok lettek hozzáadva az SqlClient streaming támogatásához.
Tipp.
Az új aszinkron metódusok nem szükségesek Asynchronous Processing=true
a kapcsolati sztring.
Szinkron aszinkron Csatlakozás ion Open
Egy meglévő alkalmazást frissíthet az új 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 új aszinkron funkció használatára konvertált program a következőképpen nézne ki:
using System;
using System.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("Data Source=(local); Initial Catalog=NorthWind; Integrated Security=SSPI")) {
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]);
}
}
}
Az új aszinkron funkció hozzáadása egy meglévő alkalmazásban (régi és új minták keverése)
Új aszinkron képesség (Sql Csatlakozás ion::OpenAsync) hozzáadására is lehetőség van 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.
using System;
using System.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("Data Source=(local); Initial Catalog=NorthWind; Integrated Security=SSPI");
// conn.Open();
// SqlCommand cmd = new SqlCommand("select top 2 * from orders", conn);
// IAsyncResult ia = cmd.BeginExecuteReader(productList, cmd);
AsyncCallback productList = new AsyncCallback(ProductList);
SqlConnection conn = new SqlConnection("Data Source=(local); Initial Catalog=NorthWind; Integrated Security=SSPI");
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.
using System;
using System.Data.Common;
using System.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 = "System.Data.SqlClient";
Task task = PerformDBOperationsUsingProviderModel(builder.ConnectionString, provider);
task.Wait();
}
}
SQL-tranzakciók és az új aszinkron funkció használata
using System;
using System.Data.SqlClient;
using System.Threading.Tasks;
class Program {
static void Main() {
string connectionString =
"Persist Security Info=False;Integrated Security=SSPI;database=Northwind;server=(local)";
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 egy elosztott tranzakciót:
using System;
using System.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);
}
}
}
}
}
}
Aszinkron művelet megszakítása
Az aszinkron kéréseket a CancellationToken.
using System;
using System.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 (exception.InnerException is SqlException) {
Console.WriteLine("Operation canceled");
}
else {
throw;
}
}
}
static async Task CancellingAsynchronousOperations(CancellationToken cancellationToken) {
using (SqlConnection connection = new SqlConnection("Server=(local);Integrated Security=true")) {
await connection.OpenAsync(cancellationToken);
SqlCommand command = new SqlCommand("WAITFOR DELAY '00:10:00'", connection);
await command.ExecuteNonQueryAsync(cancellationToken);
}
}
}
}
Aszinkron műveletek az SqlBulkCopyval
Aszinkron képességeket is hozzáadtunk a System.Data.SqlClient.SqlBulkCopySqlBulkCopy.WriteToServerAsync.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Odbc;
using System.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)";
// Replace the connection string if needed, for instance to connect to SQL Express: @"Server=(local)\SQLEXPRESS;Database=Demo;Integrated Security=true"
// static string connectionString = @"Server=(localdb)\V11.0;Database=Demo";
static string connectionString = @"Server=(local);Database=Demo;Integrated Security=true";
// static string odbcConnectionString = @"Driver={SQL Server};Server=(localdb)\V11.0;UID=oledb;Pwd=[PLACEHOLDER];Database=Demo";
static string odbcConnectionString = @"Driver={SQL Server};Server=(local);Database=Demo;Integrated Security=true";
// static string marsConnectionString = @"Server=(localdb)\V11.0;Database=Demo;MultipleActiveResultSets=true;";
static string marsConnectionString = @"Server=(local);Database=Demo;MultipleActiveResultSets=true;Integrated Security=true";
// Replace the Server name with your actual sql azure server name and User ID/Password
static string azureConnectionString = @"Server=SqlAzure;User ID=myUserID;Password=myPassword;Database=Demo";
static void Main(string[] args) {
SynchronousSqlBulkCopy();
AsyncSqlBulkCopy().Wait();
MixSyncAsyncSqlBulkCopy().Wait();
AsyncSqlBulkCopyNotifyAfter().Wait();
AsyncSqlBulkCopyDataRows().Wait();
// AsyncSqlBulkCopySqlServerToSqlAzure().Wait();
// AsyncSqlBulkCopyCancel().Wait();
AsyncSqlBulkCopyMARS().Wait();
}
// 3.1.1 Synchronous bulk copy in .NET Framework 4.5
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 in .NET Framework 4.5
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 = "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.5 Copying data from SQL Server to SQL Azure in .NET Framework 4.5
//private static async Task AsyncSqlBulkCopySqlServerToSqlAzure() {
// using (SqlConnection srcConn = new SqlConnection(connectionString))
// using (SqlConnection destConn = new SqlConnection(azureConnectionString)) {
// await srcConn.OpenAsync();
// await destConn.OpenAsync();
// using (SqlCommand srcCmd = new SqlCommand(selectStatement, srcConn)) {
// using (SqlDataReader reader = await srcCmd.ExecuteReaderAsync()) {
// string temptable = "[#" + Guid.NewGuid().ToString("N") + "]";
// 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(reader);
// }
// }
// }
// }
// }
//}
// 3.6 Cancelling an Asynchronous Operation to SQL Azure
//private static async Task AsyncSqlBulkCopyCancel() {
// CancellationTokenSource cts = new CancellationTokenSource();
// using (SqlConnection srcConn = new SqlConnection(connectionString))
// using (SqlConnection destConn = new SqlConnection(azureConnectionString)) {
// await srcConn.OpenAsync(cts.Token);
// await destConn.OpenAsync(cts.Token);
// using (SqlCommand srcCmd = new SqlCommand(selectStatement, srcConn)) {
// using (SqlDataReader reader = await srcCmd.ExecuteReaderAsync(cts.Token)) {
// string temptable = "[#" + Guid.NewGuid().ToString("N") + "]";
// using (SqlCommand destCmd = new SqlCommand(string.Format(createDestTableStatement, temptable), destConn)) {
// await destCmd.ExecuteNonQueryAsync(cts.Token);
// using (SqlBulkCopy bcp = new SqlBulkCopy(destConn)) {
// bcp.DestinationTableName = temptable;
// await bcp.WriteToServerAsync(reader, cts.Token);
// //Cancel Async SqlBulCopy Operation after 200 ms
// cts.CancelAfter(200);
// }
// }
// }
// }
// }
//}
// 3.7 Using Async.Net and MARS
private static async Task AsyncSqlBulkCopyMARS() {
using (SqlConnection marsConn = new SqlConnection(marsConnectionString)) {
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 több parancs használata a MARS használatával
A példa egyetlen kapcsolatot nyit meg az AdventureWorks-adatbázissal . Objektum használatával SqlCommand létrejön egy SqlDataReader . Az olvasó használatakor megnyílik egy másodperc 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.
Feljegyzés
Az alábbi példa az SQL Serverhez mellékelt AdventureWorks-mintaadatbázist 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 sztring a környezethez.
using System;
using System.Data;
using System.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());
}
}
}
}
}
}
private static string GetConnectionString() {
// To avoid storing the connection string in your code, you can retrieve it from a configuration file.
return "Data Source=(local);Integrated Security=SSPI;Initial Catalog=AdventureWorks;MultipleActiveResultSets=True";
}
}
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 sp_getbindtoken és sp_bindsession rendszer által tárolt eljárások használatára.
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 SqlDataReaderlátogat meg. A rendszer számítással határozza meg, hogy mi legyen az új OnOrderQty . A harmadik parancsobjektum ezután a ProductVendor tábla frissítésére szolgál 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.
Feljegyzés
Az alábbi példa az SQL Serverhez mellékelt AdventureWorks-mintaadatbázist 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 sztring a környezethez.
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.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: ", totalRecordsUpdated.ToString());
await Task.Run(() => updateTx.Rollback());
Console.WriteLine("Transaction Rolled Back");
}
}
private static string GetConnectionString() {
// To avoid storing the connection string in your code, you can retrieve it from a configuration file.
return "Data Source=(local);Integrated Security=SSPI;Initial Catalog=AdventureWorks;MultipleActiveResultSets=True";
}
}