Exécution d’opérations asynchrones dans SQL Server Native Client

S’applique à :SQL ServerAzure SQL DatabaseAzure SQL Managed InstanceAzure Synapse AnalyticsAnalytics Platform System (PDW)

Important

SQL Server Native Client (souvent abrégé en SNAC) a été supprimé dans SQL Server 2022 (16.x) et SQL Server Management Studio 19 (SSMS). SQL Server Native Client (SQLNCLI ou SQLNCLI11) et le fournisseur Microsoft OLE DB pour SQL Server (SQLOLEDB) hérité ne sont pas recommandés dans les nouveaux développements. Utilisez à la place le nouveau Microsoft OLE DB Driver (MSOLEDBSQL) pour SQL Server ou le Microsoft ODBC Driver for SQL Server le plus récent. Pour SQLNCLI qui est fourni en tant que composant du moteur de base de données SQL Server (versions 2012 à 2019), consultez cette exception du cycle de vie du support.

SQL Server permet aux applications d'effectuer des opérations de base de données asynchrones. Le traitement asynchrone permet aux méthodes d'être retournées immédiatement sans blocage du thread appelant. Cela rend le multithreading plus puissant et plus souple, sans obliger le développeur à créer explicitement des threads ou à gérer la synchronisation. Les applications requièrent un traitement asynchrone lors de l'initialisation d'une connexion de base de données, ou lors de l'initialisation du résultat de l'exécution d'une commande.

Ouverture et fermeture d'une connexion de base de données

Lorsque vous utilisez le fournisseur OLE DB SQL Server Native Client, les applications conçues pour initialiser un objet de source de données peuvent définir de manière asynchrone le bit DBPROPVAL_ASYNCH_INITIALIZE dans la propriété DBPROP_INIT_ASYNCH avant d’appeler IDBInitialize ::Initialize. Quand cette propriété est définie, le fournisseur est retourné immédiatement à partir de l’appel à Initialize avec la valeur S_OK si l’opération s’est effectuée immédiatement ou DB_S_ASYNCHRONOUS si l’initialisation se poursuit de façon asynchrone. Les applications peuvent interroger l’interface IDBAsynchStatus ou ISSAsynchStatus sur l’objet de source de données, puis appeler IDBAsynchStatus ::GetStatus ouISSAsynchStatus ::WaitForAsynchCompletion pour obtenir l’état de l’initialisation.

De plus, la propriété SSPROP_ISSAsynchStatus a été ajoutée au jeu de propriétés DBPROPSET_SQLSERVERROWSET. Les fournisseurs qui prennent en charge l’interface ISSAsynchStatus doivent implémenter cette propriété avec la valeur VARIANT_TRUE.

IDBAsynchStatus::Abort ou ISSAsynchStatus::Abort peut être appelée pour annuler l’appel asynchrone à Initialize. Le consommateur doit demander explicitement l'initialisation de la source de données asynchrone. Sinon, IDBInitialize::Initialize n’est pas retournée tant que l’objet source de données n’est pas complètement initialisé.

Remarque

Les objets de source de données utilisés pour le regroupement de connexions ne peuvent pas appeler l’interface ISSAsynchStatus dans le fournisseur OLE DB SQL Server Native Client. L’interface ISSAsynchStatus n’est pas exposée pour les objets sources de données regroupés.

Si une application force explicitement l’utilisation du moteur de curseur, IOpenRowset::OpenRowset et IMultipleResults::GetResult ne prennent pas en charge le traitement asynchrone.

En outre, la dll proxy/stub de communication à distance (dans MDAC 2.8) ne peut pas appeler l’interface ISSAsynchStatus dans SQL Server Native Client. L’interface ISSAsynchStatus n’est pas exposée via la communication à distance.

Les composants du service ne prennent pas en charge ISSAsynchStatus.

Exécution et initialisation de l'ensemble de lignes

Les applications conçues pour ouvrir de façon asynchrone le résultat de l'exécution d'une commande peuvent définir DBPROPVAL_ASYNCH_INITIALIZE dans la propriété DBPROP_ROWSET_ASYNCH. Lors de la définition de ce bit avant d’appeler IDBInitialize::Initialize, ICommand::Execute, IOpenRowset::OpenRowset ou IMultipleResults::GetResult, l’argument riid doit avoir pour valeur IID_IDBAsynchStatus, IID_ISSAsynchStatus ou IID_IUnknown.

La méthode est retournée immédiatement avec S_OK si l’initialisation de l’ensemble de lignes s’effectue immédiatement (ou avec DB_S_ASYNCHRONOUS si l’ensemble de lignes poursuit son initialisation de façon asynchrone) avec ppRowset défini en fonction de l’interface demandée sur l’ensemble de lignes. Pour le fournisseur OLE DB SQL Server Native Client, cette interface peut uniquement être IDBAsynchStatus ou ISSAsynchStatus. Jusqu’à ce que l’ensemble de lignes soit entièrement initialisé, cette interface se comporte comme si elle était dans un état suspendu ; par ailleurs, l’appel de QueryInterface pour les interfaces autres qu’IID_IDBAsynchStatus ou qu’IID_ISSAsynchStatus peut retourner E_NOINTERFACE. À moins que le consommateur ne demande explicitement un traitement asynchrone, l'ensemble de lignes est initialisé de façon synchrone. Toutes les interfaces demandées sont disponibles quand IDBAsynchStaus::GetStatus ou ISSAsynchStatus::WaitForAsynchCompletion est retournée avec l’indication que l’opération asynchrone a été effectuée. Cela ne signifie pas nécessairement que l'ensemble de lignes soit entièrement rempli, mais il est prêt et complètement fonctionnel.

Si la commande exécutée ne retourne pas d’ensemble de lignes, elle est quand même retournée immédiatement avec un objet qui prend en charge IDBAsynchStatus.

S'il vous faut obtenir plusieurs résultats à partir de l'exécution d'une commande asynchrone, vous devez :

  • définir DBPROPVAL_ASYNCH_INITIALIZE dans la propriété DBPROP_ROWSET_ASYNCH, avant d'exécuter la commande ;

  • appeler ICommand::Execute et demander IMultipleResults.

Les interfaces IDBAsynchStatus et ISSAsynchStatus peuvent ensuite être obtenues en interrogeant l’interface de résultats multiples via QueryInterface.

Quand l’exécution de la commande est terminée, IMultipleResults peut être utilisé normalement à une exception près par rapport au traitement synchrone : DB_S_ASYNCHRONOUS peut être retourné, auquel cas IDBAsynchStatus ou ISSAsynchStatus peut être utilisée pour déterminer le moment où l’opération est achevée.

Exemples

Dans l'exemple suivant, l'application appelle une méthode non bloquante, effectue d'autres traitements, puis retourne au traitement des résultats. ISSAsynchStatus::WaitForAsynchCompletion attend l’objet d’événement interne jusqu’à ce que l’opération s’exécutant de manière asynchrone soit terminée ou que le délai spécifié par dwMilisecTimeOut soit passé.

// Set the DBPROPVAL_ASYNCH_INITIALIZE bit in the   
// DBPROP_ROWSET_ASYNCH property before calling Execute().  
  
DBPROPSET CmdPropset[1];  
DBPROP CmdProperties[1];  
  
CmdPropset[0].rgProperties = CmdProperties;  
CmdPropset[0].cProperties = 1;  
CmdPropset[0].guidPropertySet = DBPROPSET_ROWSET;  
  
// Set asynch mode for command.  
CmdProperties[0].dwPropertyID = DBPROP_ROWSET_ASYNCH;  
CmdProperties[0].vValue.vt = VT_I4;  
CmdProperties[0].vValue.lVal = DBPROPVAL_ASYNCH_INITIALIZE;  
CmdProperties[0].dwOptions = DBPROPOPTIONS_REQUIRED;  
  
hr = pICommandProps->SetProperties(1, CmdPropset);  
  
hr = pICommand->Execute(  
   pUnkOuter,  
   IID_ISSAsynchStatus,  
   pParams,  
   pcRowsAffected,  
   (IUnknown**)&pISSAsynchStatus);  
  
if (hr == DB_S_ASYNCHRONOUS)  
{  
   // Do some work here...  
  
   hr = pISSAsynchStatus->WaitForAsynchCompletion(dwMilisecTimeOut);  
   if ( hr == S_OK)  
   {  
      hr = pISSAsynchStatus->QueryInterface(IID_IRowset, (void**)&pRowset);  
      pISSAsynchStatus->Release();  
   }  
}  

ISSAsynchStatus::WaitForAsynchCompletion attend l’objet d’événement interne jusqu’à ce que l’opération s’exécutant de manière asynchrone soit terminée ou que la valeur dwMilisecTimeOut soit passée.

L'exemple suivant illustre le traitement asynchrone avec plusieurs jeux de résultats :

DBPROP CmdProperties[1];  
  
// Set asynch mode for command.  
CmdProperties[0].dwPropertyID = DBPROP_ROWSET_ASYNCH;  
CmdProperties[0].vValue.vt = VT_I4;  
CmdProperties[0].vValue.lVal = DBPROPVAL_ASYNCH_INITIALIZE;  
  
hr = pICommand->Execute(  
   pUnkOuter,  
   IID_IMultipleResults,  
   pParams,  
   pcRowsAffected,  
   (IUnknown**)&pIMultipleResults);  
  
// Use GetResults for ISSAsynchStatus.  
hr = pIMultipleResults->GetResult(IID_ISSAsynchStatus, (void **) &pISSAsynchStatus);  
  
if (hr == DB_S_ASYNCHRONOUS)  
{  
   // Do some work here...  
  
   hr = pISSAsynchStatus->WaitForAsynchCompletion(dwMilisecTimeOut);  
   if (hr == S_OK)  
   {  
      hr = pISSAsynchStatus->QueryInterface(IID_IRowset, (void**)&pRowset);  
      pISSAsynchStatus->Release();  
   }  
}  

Pour empêcher tout blocage, le client peut vérifier l'état d'une opération asynchrone en cours d'exécution, comme le montre l'exemple suivant :

// Set the DBPROPVAL_ASYNCH_INITIALIZE bit in the   
// DBPROP_ROWSET_ASYNCH property before calling Execute().  
hr = pICommand->Execute(  
   pUnkOuter,  
   IID_ISSAsynchStatus,  
   pParams,  
   pcRowsAffected,  
   (IUnknown**)&pISSAsynchStatus);   
  
if (hr == DB_S_ASYNCHRONOUS)  
{  
   do{  
      // Do some work...  
      hr = pISSAsynchStatus->GetStatus(DB_NULL_HCHAPTER, DBASYNCHOP_OPEN, NULL, NULL, &ulAsynchPhase, NULL);  
   }while (DBASYNCHPHASE_COMPLETE != ulAsynchPhase)  
   if SUCCEEDED(hr)  
   {  
      hr = pISSAsynchStatus->QueryInterface(IID_IRowset, (void**)&pRowset);  
   }  
   pIDBAsynchStatus->Release();  
}  

L'exemple suivant montre comment vous pouvez annuler l'opération asynchrone en cours d'exécution :

// Set the DBPROPVAL_ASYNCH_INITIALIZE bit in the   
// DBPROP_ROWSET_ASYNCH property before calling Execute().  
hr = pICommand->Execute(  
   pUnkOuter,  
   IID_ISSAsynchStatus,  
   pParams,  
   pcRowsAffected,  
   (IUnknown**)&pISSAsynchStatus);  
  
if (hr == DB_S_ASYNCHRONOUS)  
{  
   // Do some work...  
   hr = pISSAsynchStatus->Abort(DB_NULL_HCHAPTER, DBASYNCHOP_OPEN);  
}  

Voir aussi

Fonctionnalités de SQL Server Native Client
Propriétés et comportements des ensembles de lignes
ISSAsynchStatus (OLE DB)