Je sais que c'est un ancien article; tout en ajoutant mon 2c basé sur nos récentes (très concluants) la mise en œuvre et d'essai: D
Pour répondre aux questions de l'OP:
- Si vous n'appelez pas EndExecuteNonQuery, BeginExecuteNonQuery exécutera la procédure, mais l'opération sera annulée dès que la clause using disposera de votre connexion sql. Par conséquent, ce n'est pas plausible.
- Si vous appelez BeginExecuteNonQuery en utilisant un délégué, en créant un nouveau thread etc. et que vous n'appelez pas EndExecuteNonQuery, il y a de fortes chances que vous receviez des fuites de mémoire en fonction de ce qui se passe dans votre procédure stockée. (Plus à ce sujet plus tard).
- L'appel d'une procédure stockée et ne pas attendre la fin de l'appel, dans la mesure où nos tests se sont déroulés, n'est pas possible. Indépendamment du multitâche, quelque chose devra attendre quelque part.
à notre solution:
Réfs: BeginExecuteNonQuery -> BENQ, EndExecuteNonQuery -> EENQ
Cas d'utilisation:
Nous avons un service Windows (C#) qui utilise la bibliothèque .Net TPL. Nous avions besoin de charger des données avec une procédure stockée d'une base de données à l'autre au moment de l'exécution, en fonction d'une requête add-hoc que le service prend en charge. Notre procédure stockée comportait une transaction interne et une gestion des exceptions avec des blocs try catch.
premier essai:
Pour notre premier essai nous a mis en œuvre une solution trouvée ici MS Solution dans cet exemple, vous verrez que MS choisit d'appeler BENQ met alors en oeuvre une boucle while pour bloquer l'exécution et appelle ensuite EENQ. Cette solution a été implémentée principalement si vous n'avez pas besoin d'une méthode de rappel. Le problème avec cette solution est que seul BENQ est ignorant des délais d'attente de connexion SQL. L'EENQ expirera. Donc, pour une requête de longue durée (ce qui est la raison pour laquelle vous utilisez BENQ), vous serez coincé dans le temps et une fois l'opération terminée et vous appelez EENQ, vous obtiendrez une connexion de délai d'attente sql.
Deuxième essai:
Pour notre deuxième essai nous avons pensé ok permet donc appeler BENQ, puis ajoutez un certain temps pour que nous ne fermons pas notre connexion SQL et ne jamais appeler EENQ. Cela a fonctionné, jusqu'à ce qu'une exception ait été levée dans notre procédure stockée. Parce que nous n'avons jamais appelé EENQ, l'opération n'a jamais été terminée et l'exception n'a jamais grimpé jusqu'à notre code. Par conséquent, nous étions coincés dans une fuite de boucle/thread/mémoire pour toujours.
Troisième essai: (La solution)
Pour notre troisième essai nous avons pensé appeler BENQ, puis directement après EENQ d'appel. Ce qui s'est passé, c'est qu'EENQ a effectivement bloqué l'exécution dans le thread jusqu'à la fin de l'opération. Lorsqu'une exception s'est produite dans la procédure stockée, elle a été interceptée. Lorsque la requête était longue, EENQ ne renvoyait pas d'exception de dépassement de délai et, dans tous les cas, notre objet de connexion sql était éliminé ainsi que notre thread.
Voici quelques extraits de notre code:
nous ouvrons ici un nouveau fil pour la méthode qui appelle la procédure stockée.
//Call the load data stored procedure. As this stored procedure can run longer we start it in its own thread.
Task.Factory.StartNew(() => ClassName.MethodName(Parameters));
Ceci est le code dans la méthode que nous utilisons pour appeler la procédure stockée.
//Because this is a long running stored procedure, we start is up in a new thread.
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[ConfigurationManager.AppSettings["ConnectionStringName"]].ConnectionString))
{
try
{
//Create a new instance SqlCommand.
SqlCommand command = new SqlCommand(ConfigurationManager.AppSettings["StoredProcedureName"], conn);
//Set the command type as stored procedure.
command.CommandType = CommandType.StoredProcedure;
//Create input parameters.
command.Parameters.Add(CreateInputParam("@Param1", SqlDbType.BigInt, Param1));
command.Parameters.Add(CreateInputParam("@Param2", SqlDbType.BigInt, Param3));
command.Parameters.Add(CreateInputParam("@Param3", SqlDbType.BigInt, Param3));
//Open up the sql connection.
conn.Open();
//Create a new instance of type IAsyncResult and call the sp asynchronously.
IAsyncResult result = command.BeginExecuteNonQuery();
//When the process has completed, we end the execution of the sp.
command.EndExecuteNonQuery(result);
}
catch (Exception err)
{
//Write to the log.
}
}
J'espère que cette réponse sauvera quelqu'un de mal de tête: D Nous avons testé cela minutieusement et n'avons pas rencontré de problèmes.
Bonne codification!
hmm, je suppose que je vais aller avec le ThreadPool.QueueUserWorkItem, mais il semble juste une perte que je dois utiliser un thread dans mon application pour surveiller quelque chose que je ne me soucie pas. Je veux juste donner une commande à sqlServer et l'oublier. Il semble que j'ai une incompréhension fondamentale sur le fonctionnement des connexions au db car je ne comprends vraiment pas pourquoi il n'est pas possible de le faire. Quelqu'un at-il un lien qui explique pourquoi cela n'est pas possible? –
BeginExecuteNonQuery utilise quand même le ThreadPool, donc vous ne faites rien ce qu'il ne ferait pas autrement. La raison pour laquelle vous ne pouvez pas compter sur ceci est que BeginExecuteNonQuery ne bloque pas jusqu'à ce que la requête se termine, vous pouvez fermer la connexion pendant que la requête est toujours en cours en arrière-plan. – SLaks