2012-02-02 5 views
6

System.Data.SqlClient.SqlCommand ha metodiSystem.Data.IDbCommand e esecuzione asincrona?

BeginExecuteNonQuery 
BeginExecuteReader 
BeginExecuteXmlReader 

e

EndExecuteNonQuery 
EndExecuteReader 
EndExecuteXmlReader 

per l'esecuzione asincrona.

System.Data.IDbCommand Basta

ExecuteNonQuery 
ExecuteReader 
ExecuteXmlReader 

che sono solo operazioni sincrone.

Esiste un'interfaccia per operazioni asincrone?
Inoltre, perché non esiste BeginExecuteScalar?

risposta

1

IDbCommand non hanno le cominciare/metodi asincroni fine perché non esistevano ancora in originale .NET 1.1 versione di ADO.NET, e quando i metodi asincroni erano added in .NET 2.0, sarebbe stato un cambiamento sostanziale aggiungerli a IDbCommand (l'aggiunta di membri a un'interfaccia è una modifica irrisolta per gli implementatori di quell'interfaccia).

Non so perché BeginExecuteScalar non esiste, ma può essere implementato come metodo di estensione che si estende attorno a BeginExecuteReader. Comunque in .NET 4.5 ora abbiamo ExecuteScalarAsync che è più facile da usare.

+0

Non vedo 'IDbCommand.ExecuteScalarAsync()'. Cosa [documentazione] (https://msdn.microsoft.com/en-us/library/system.data.idbcommand (v = vs.110) .aspx) stai guardando? – binki

+3

Oh, stavi dicendo ['SqlCommand.ExecuteScalarAsync()'] (https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executescalarasync%28v=vs.110%29. aspx). Io vedo. – binki

+1

@binki o ['DbCommand.ExecuteScalarAsync()'] (https://msdn.microsoft.com/en-us/library/hh223677 (v = vs.110) .aspx), che non è abbastanza buono come averlo sull'interfaccia, ma supporta alcuni polimorfismi. – phoog

-5

No, non ci sono interfacce per loro

Il motivo per cui non c'è un BeginExecuteScalar è perché probabilmente non avrete bisogno di una chiamata asincrona per ottenere un singolo valore indietro che dovrebbe essere molto veloce

+7

Questo "un valore" potrebbe coinvolgere 50 tabelle, un paio di visualizzazioni, forse alcuni proc memorizzati ecc. Probabilmente non è la ragione :) – Onkelborg

+0

Questa è l'unica ragione che posso dare a me stesso e penso che sia abbastanza richiudibile..Se per ottenere un valore dal tuo db devi coinvolgere 50 tabelle c'è qualcosa di sbagliato nel tuo schema e una chiamata asincrona non aiuterà :-) –

+0

@Massimiliano Peluso: Concesso nel 95% dei casi hai ragione. Ma se si dispone di uno schema di questo tipo, non è possibile modificarlo in tempi brevi, il che aumenta solo la necessità di un metodo asincrono. –

0

È può implementare un comportamento asincrono dal tuo codice personalizzato, dal momento che non è così complicato, come per la tua domanda - non ci sono operazioni asincrone standard per i tuoi obiettivi.

3

In realtà, la creazione di un comportamento asincrono equivalente a BeginExecuteNonQuery, EndExecuteNonQuery, ecc. Sarebbe un'attività piuttosto difficile. L'implementazione di queste API è di gran lunga superiore alla semplice generazione di un thread separato, in attesa della risposta del database e del richiamo del callback. Fanno affidamento sulla sovrapposizione di I/O e offrono una migliore economia di thread. Non vengono consumati ulteriori thread per la durata dell'hop di rete, l'elaborazione del database del comando, che è probabilmente il 99% del tempo totale trascorso per la chiamata. Per un paio di chiamate non fa alcuna differenza, ma quando si progetta un server ad alta velocità, l'economia di thread diventa molto importante.

Mi chiedevo perché mancava BeginExecuteScalar. Inoltre, la maggior parte degli altri provider, incluso ad esempio ODP.Net, non ha API asincrone!

E sì, non esiste un'interfaccia per le operazioni asincrone.

1

Anche se si sta recuperando "un valore", la maggior parte del tempo verrà speso su 1) hop di rete sul server di database, 2) comando di esecuzione del server di database. Molto più tempo di quello che spenderai per dire di leggere 1000 record nel set di dati. Quindi, sono d'accordo, non è chiaro il motivo per cui non c'è BeginExecuteScalar ...

2

Per risolvere esattamente questo problema ho creato uno shim che chiama i metodi asincroni se esistono su IDbConnection.IDbCommand/IDataReader o chiama i metodi regolari se don ' t.

Fonte: https://github.com/ttrider/IDbConnection-Async

NuGet: https://www.nuget.org/packages/IDbConnection-Async/

Esempio:

 using (IDbConnection connection = new SqlConnection(connectionString)) 
     { 
      await connection.OpenAsync(); 

      IDbCommand command = connection.CreateCommand(); 
      command.CommandText = "SELECT Name FROM Person;"; 
      using (IDataReader reader = await command.ExecuteReaderAsync()) 
      { 
       do 
       { 
        while (await reader.ReadAsync()) 
        { 
         if (!await reader.IsDBNullAsync(0)) 
         { 
          var name = reader.GetFieldValueAsync<string>(0); 
          Assert.IsNotNull(name); 
         } 
        } 
       } while (await reader.NextResultAsync()); 
      } 
     } 
1

Si consiglia di trattare DbCommand e i suoi amici come se fossero interfacce quando si utilizzano API di database. Per semplificare la generalizzazione di un'API su vari provider di database, DbCommand raggiunge lo stesso valore di IDbCommand -o, discutibilmente, meglio, perché include tecnologie più recenti come ad esempio await in grado Task*Async() membri.

MS non può aggiungere nuovi metodi con nuove funzionalità a IDbCommand. Se dovessero aggiungere un metodo a IDbCommand, si tratta di una modifica irrisolta perché chiunque è libero di implementare tale interfaccia nel proprio codice e MS ha fatto molti sforzi per preservare la compatibilità con API e API nel framework. Se espandessero le interfacce in una versione di .net, il codice cliente che in precedenza funzionava avrebbe smesso di compilare e gli assembly esistenti che non venivano ricompilati avrebbero iniziato a riscontrare errori di runtime. Inoltre, non possono aggiungere i metodi appropriati *Async() o Begin*() tramite i metodi di estensione senza fare brutto casting a DbCommand dietro le quinte (che è una cattiva pratica in sé, rompendo la sicurezza del tipo e introducendo inutilmente il cast dinamico di runtime).

D'altra parte, MS può aggiungere nuovi metodi virtuali a DbCommand senza interrompere ABI. L'aggiunta di nuovi metodi a una classe base potrebbe essere considerata la rottura dell'API (tempo di compilazione, non altrettanto grave da rompere come runtime) perché se hai ereditato DbCommand e hai aggiunto un membro con lo stesso nome, inizierai a ricevere l'avviso CS0108: 'member1' hides inherited member 'member2'. Use the new keyword if hiding was intended.) . Pertanto, DbCommand può ottenere le nuove funzionalità con un impatto minimo sul consumo di codice che segue le buone pratiche (ad esempio, la maggior parte delle cose continuerà a funzionare finché non funziona contro il sistema di tipi e metodi di chiamata utilizzando qualcosa come myCommand.GetType().GetMethods()[3].Invoke(myCommand, …)).

Una possibile strategia che MS avrebbe potuto utilizzare per supportare le persone a cui piacciono le interfacce sarebbe stata quella di introdurre nuove interfacce con nomi come IAsyncDbCommand e di implementarle con DbCommand. Non l'hanno fatto. Non so perché, ma probabilmente non lo hanno fatto perché aumenterebbe la complicazione e l'alternativa di consumare direttamente DbCommand fornisce la maggior parte dei vantaggi per il consumo di interfacce con pochi aspetti negativi. Sì, sarebbe un lavoro con poco ritorno.