Zaman uyumsuz programlama
Bu makalede, .NET Framework 4.5'te kullanıma sunulan zaman uyumsuz programlama işlevselliğini desteklemek için yapılan geliştirmeler de dahil olmak üzere SQL Server (SqlClient) için .NET Framework Veri Sağlayıcısı zaman uyumsuz programlama desteği ele alınmaktadır.
Eski zaman uyumsuz programlama
.NET Framework 4.5'in öncesinde, SqlClient ile zaman uyumsuz programlama aşağıdaki yöntemler ve Asynchronous Processing=true
bağlantı özelliğiyle yapıldı:
Bu işlevsellik .NET Framework 4.5'teki SqlClient'da kaldı.
İpucu
.NET Framework 4.5'te başlayarak, bu eski yöntemler artık bağlantı dizesi gerektirmezAsynchronous Processing=true
.
.NET Framework 4.5'te zaman uyumsuz programlama özellikleri eklendi
Zaman uyumsuz programlama özelliği, kodu zaman uyumsuz hale getirmek için basit bir teknik sağlar. .NET Framework 4.5'te kullanıma sunulan zaman uyumsuz programlama özelliği hakkında daha fazla bilgi için bkz:
- C'de zaman uyumsuz programlama#
- Async ve Await ile Zaman Uyumsuz Programlama (Visual Basic)
- .NET Framework 4.5'te SqlDataReader'ın yeni zaman uyumsuz yöntemlerini kullanma (Bölüm 1)
- .NET Framework 4.5'te SqlDataReader'ın yeni zaman uyumsuz yöntemlerini kullanma (Bölüm 2)
Kullanıcı arabiriminiz yanıt vermiyorsa veya sunucunuz ölçeklendirilmiyorsa, kodunuzun daha zaman uyumsuz olması gerekebilir. Zaman uyumsuz kod yazmak, zaman uyumsuz işlem tamamlandıktan sonra oluşan mantığı ifade etmek için geleneksel olarak bir geri çağırma (devamlılık olarak da adlandırılır) yüklemeyi içerir. Bu, zaman uyumlu kodla karşılaştırıldığında zaman uyumsuz kodun yapısını karmaşıklaştırır.
Artık geri çağırmaları kullanmadan ve kodunuzu birden çok yönteme veya lambda ifadesine bölmeden zaman uyumsuz yöntemlere çağrı yapabilirsiniz.
Değiştirici, async
bir yöntemin zaman uyumsuz olduğunu belirtir. Bir async
yöntem çağrılırken bir görev döndürülür. İşleç await
bir göreve uygulandığında geçerli yöntem hemen çıkar. Görev tamamlandığında, yürütme aynı yöntemde devam eder.
Uyarı
Bir uygulama bağlantı dizesi anahtar sözcüğünü Context Connection
de kullanıyorsa zaman uyumsuz çağrılar desteklenmez.
Bir async
yöntemi çağırmak ek iş parçacığı ayırmaz. Sonunda mevcut G/Ç tamamlama iş parçacığını kısa bir süre kullanabilir.
Zaman uyumsuz programlamayı desteklemek için .NET Framework 4.5'e aşağıdaki yöntemler eklendi:
- 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
SqlClient Akış Desteği desteğine diğer zaman uyumsuz üyeler eklendi.
İpucu
Daha yeni zaman uyumsuz yöntemler bağlantı dizesi gerektirmezAsynchronous Processing=true
.
Zaman uyumsuz bağlantıyla zaman uyumlu açık
Daha eski bir uygulamayı daha yeni zaman uyumsuz özelliği kullanacak şekilde yükseltebilirsiniz. Örneğin, bir uygulamanın zaman uyumlu bir bağlantı algoritması olduğunu ve veritabanına her bağlandığında ui iş parçacığını engellediğini ve bağlandıktan sonra uygulamanın yeni oturum açan diğer kullanıcılara sinyal veren saklı bir yordam çağırdığını varsayalım.
using SqlConnection conn = new SqlConnection("...");
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("StoredProcedure_Logon", conn))
{
cmd.ExecuteNonQuery();
}
}
Daha yeni zaman uyumsuz işlevselliği kullanacak şekilde dönüştürüldüğünde, program şöyle görünür:
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("...")) {
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]);
}
}
}
Mevcut bir uygulamaya yeni zaman uyumsuz özelliği ekleme (eski ve yeni desenleri karıştırma)
Mevcut zaman uyumsuz mantığı değiştirmeden yeni zaman uyumsuz özellik (SqlConnection::OpenAsync
) eklemek de mümkündür. Örneğin, bir uygulama şu anda kullanıyorsa:
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);
Mevcut algoritmayı önemli ölçüde değiştirmeden yeni zaman uyumsuz deseni kullanmaya başlayabilirsiniz.
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("...");
conn.OpenAsync().ContinueWith((task) => {
SqlCommand cmd = new SqlCommand("select top 2 * from orders", conn);
IAsyncResult ia = cmd.BeginExecuteReader(productList, cmd);
}, TaskContinuationOptions.OnlyOnRanToCompletion);
}
}
Temel sağlayıcı modelini ve yeni zaman uyumsuz özelliği kullanın
Farklı veritabanlarına bağlanabilen ve sorgu yürütebilen bir araç oluşturmanız gerekebilir. Temel sağlayıcı modelini ve yeni zaman uyumsuz özelliği kullanabilirsiniz.
Dağıtılmış işlemleri kullanmak için sunucuda Microsoft Dağıtılmış İşlem Denetleyicisi'nin (MSDTC) etkinleştirilmesi gerekir. MSDTC'yi etkinleştirme hakkında bilgi için bkz . Web Sunucusunda MSDTC'yi Etkinleştirme.
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();
}
}
Önemli
Microsoft, kullanılabilir en güvenli kimlik doğrulama akışını kullanmanızı önerir. Azure SQL'e bağlanıyorsanız önerilen kimlik doğrulama yöntemi Azure kaynakları için Yönetilen Kimlikler'dir.
SQL işlemlerini ve yeni zaman uyumsuz özelliği kullanma
using System;
using System.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 işlemlerini ve yeni zaman uyumsuz özelliği kullanma
Kurumsal bir uygulamada, birden çok veritabanı sunucusu arasındaki işlemleri etkinleştirmek için bazı senaryolara dağıtılmış işlemler eklemeniz gerekebilir. Ad alanını System.Transactions kullanabilir ve dağıtılmış bir işlemi aşağıdaki gibi listeleyebilirsiniz:
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);
}
}
}
}
}
}
Önemli
Microsoft, kullanılabilir en güvenli kimlik doğrulama akışını kullanmanızı önerir. Azure SQL'e bağlanıyorsanız önerilen kimlik doğrulama yöntemi Azure kaynakları için Yönetilen Kimlikler'dir.
Zaman uyumsuz işlemi iptal etme
kullanarak zaman uyumsuz bir isteği CancellationTokeniptal edebilirsiniz.
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("...")) {
await connection.OpenAsync(cancellationToken);
SqlCommand command = new SqlCommand("WAITFOR DELAY '00:10:00'", connection);
await command.ExecuteNonQueryAsync(cancellationToken);
}
}
}
}
SqlBulkCopy ile zaman uyumsuz işlemler
ile SqlBulkCopy.WriteToServerAsynczaman uyumsuz özellikler de eklendiSystem.Data.SqlClient.SqlBulkCopy.
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)";
static string connectionString = "...";
static void Main(string[] args) {
SynchronousSqlBulkCopy();
AsyncSqlBulkCopy().Wait();
MixSyncAsyncSqlBulkCopy().Wait();
AsyncSqlBulkCopyNotifyAfter().Wait();
AsyncSqlBulkCopyDataRows().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(connectionString)) {
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.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);
}
}
}
}
}
}
}
}
MARS ile birden çok komutu zaman uyumsuz olarak kullanma
Örnek, AdventureWorks veritabanına tek bir bağlantı açar. Bir SqlCommand nesne kullanılarak bir SqlDataReader oluşturulur. Okuyucu kullanıldıkçe, ikinci SqlDataReader okuyucunun WHERE yan tümcesine giriş olarak ilkinden SqlDataReader veriler kullanılarak bir saniye açılır.
Not
Aşağıdaki örnekte SQL Server ile birlikte verilen örnek AdventureWorks veritabanı kullanılmaktadır. Örnek kodda sağlanan bağlantı dizesi veritabanının yüklü ve yerel bilgisayarda kullanılabilir olduğunu varsayar. ortamınız için gerekli bağlantı dizesi değiştirin.
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());
}
}
}
}
}
}
}
MARS ile verileri zaman uyumsuz olarak okuma ve güncelleştirme
MARS, birden fazla bekleyen işlemle hem okuma işlemleri hem de veri işleme dili (DML) işlemleri için bir bağlantının kullanılmasına olanak tanır. Bu özellik, bir uygulamanın bağlantı meşgul hatalarıyla başa çıkma gereksinimini ortadan kaldırır. Buna ek olarak, MARS genellikle daha fazla kaynak tüketen sunucu tarafı imleç kullanıcısının yerini alabilir. Son olarak, birden çok işlem tek bir bağlantı üzerinde çalışabildiğinden aynı işlem bağlamını paylaşabilir ve bu da sp_getbindtoken ve sp_bindsession sistem saklı yordamlarını kullanma gereksinimini ortadan kaldırır.
Aşağıdaki Konsol uygulaması, üç SqlCommand nesneyle iki SqlDataReader nesnenin ve MARS'ın etkinleştirildiği tek SqlConnection bir nesnenin nasıl kullanılacağını gösterir. İlk komut nesnesi, kredi derecelendirmesi 5 olan satıcıların listesini alır. İkinci komut nesnesi, ikincisini belirli bir satıcının tüm ürünleriyle yüklemek SqlDataReader için öğesinden SqlDataReader sağlanan satıcı kimliğini kullanır. Her ürün kaydı ikinci SqlDataReadertarafından ziyaret edilir. Yeni OnOrderQty'nin ne olması gerektiğini belirlemek için bir hesaplama gerçekleştirilir. Üçüncü komut nesnesi daha sonra ProductVendor tablosunu yeni değerle güncelleştirmek için kullanılır. Bu işlemin tamamı, sonunda geri alınan tek bir işlem içinde gerçekleşir.
Not
Aşağıdaki örnekte SQL Server ile birlikte verilen örnek AdventureWorks veritabanı kullanılmaktadır. Örnek kodda sağlanan bağlantı dizesi veritabanının yüklü ve yerel bilgisayarda kullanılabilir olduğunu varsayar. ortamınız için gerekli bağlantı dizesi değiştirin.
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");
}
}
}