none
Errreurs lors d'un multithreading au sein d'une même transaction avec appels en base RRS feed

  • Discussion générale

  • Bonjour,

    J'ai besoin d’exécuter en parallèle, un traitement faisant plusieurs accès en base (autant en lecture qu'en écriture) dans une seule et même transaction.

    Cependant, je me heurte aléatoirement à deux erreurs :

    - Exception thrown: 'System.Data.SqlClient.SqlException' in System.Data.dll
    Additional information: Transaction context in use by another session.

    - Exception thrown: 'System.Data.SqlClient.SqlException' in System.Data.dll
    Additional information: Un DataReader associé à cette Command est déjà ouvert. Il doit d'abord être fermé.

    Voici un code d'exemple simplifié ou je reproduis le problème :

    using System;
    using System.Collections.Generic;
    using System.Data.SqlClient;
    using System.Globalization;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Transactions;

    namespace TestAsyncTran
    {
        public class Program
        {
            static void Main(string[] args)
            {
                List<Task> tasks = new List<Task>();
                using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required,
                            new TransactionOptions()
                            {
                                IsolationLevel = IsolationLevel.ReadCommitted

                            }
                , TransactionScopeAsyncFlowOption.Enabled))
                {
                    Parallel.For(0, 10, (i) => MonTraitement());
                }
            }

            private static void MonTraitement()
            {
                using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required,
                        new TransactionOptions()
                        {
                            IsolationLevel = IsolationLevel.ReadCommitted
                        }))
                {
                    try
                    {
                        MaLectureEnBase();
                        scope.Complete();
                    }
                    catch (Exception e)
                    {
                        throw e;
                    }
                }
            }

            private static System.Data.DataSet MaLectureEnBase()
            {
                string parametres = "ABC";
                string storeProcedureName = "MaProcedureStockee";
                System.Data.DataSet dsResult = new System.Data.DataSet();
                dsResult.Locale = CultureInfo.InvariantCulture;

                if (parametres != null && string.IsNullOrEmpty(parametres.Trim()) == false)
                {
                    Dictionary<string, SqlParameter> parameters = new Dictionary<string, SqlParameter>();
                    parameters.Add("@MonParametre", new SqlParameter()
                    {
                        ParameterName = "@MonParametre",
                        SqlDbType = System.Data.SqlDbType.Char,
                        Direction = System.Data.ParameterDirection.Input,
                        Size = 8000,
                        Value = parametres,
                    });

                    string cnxString = @"Persist Security Info=False;Data Source=MONSERVEUR\DEV;User ID=;Initial Catalog=MABASE;Integrated Security=SSPI";
                    using (SqlConnection m_cnx = new SqlConnection(cnxString))
                    {
                        if (m_cnx.State != System.Data.ConnectionState.Open)
                        {
                            m_cnx.Open();
                        }

                        dsResult = GetDataSet(System.Data.CommandType.StoredProcedure, storeProcedureName, parameters, m_cnx);

                        m_cnx.Close();
                    }
                }

                return dsResult;
            }

            public static System.Data.DataSet GetDataSet(System.Data.CommandType commandType, string commandText, IDictionary<string, SqlParameter> grpParameter, SqlConnection m_cnx)
            {
                using (SqlCommand cmd = new SqlCommand())
                {
                    cmd.Connection = m_cnx;
                    cmd.CommandType = commandType;
                    cmd.CommandText = commandText;
                    cmd.CommandTimeout = 60;

                    if (grpParameter != null)
                    {
                        foreach (var parameter in grpParameter.Values)
                        {
                            cmd.Parameters.Add(parameter);
                        }
                    }

                    System.Data.DataSet dataSet = new System.Data.DataSet();

                    using (SqlDataAdapter dataAdapter = new SqlDataAdapter(cmd))
                    {
                        dataAdapter.Fill(dataSet);
                    }

                    return dataSet;
                }
            }
        }
    }

    Sachant que j'ai aussi tenter :

    - d'utiliser des task plutôt que le Parallel.For

    - de réaliser simplement un "ExecuteNonQuery" plutôt que de récupérer le DataSet

    - d'utiliser une connexion partagée entre les Threads

    - d'utiliser les DependantTransaction

    Mais dans tous les cas je retombe sur ces mêmes erreurs ou, pour le 3ème point, sur un problème de statut de la connexion ou de la transaction.

    Qu'est ce qui selon vous, dans ce code, pose problème à la parallélisation ?

    Cordialement,

    Emmanuelle

    mardi 12 juillet 2016 09:32

Toutes les réponses

  • Bonjour Emmanuelle L,

    Il y a des solution proposées dans le thread suivant :
    Transaction context in use by another session
    Je vous remercie par avance de votre retour.

    Cordialement,
    Teodora


    Votez! Appel à la contribution TechNet Community Support. LE CONTENU EST FOURNI "TEL QUEL" SANS GARANTIE D'AUCUNE SORTE, EXPLICITE OU IMPLICITE. S'il vous plaît n'oubliez pas de "Marquer comme réponse" les réponses qui ont résolu votre problème. C'est une voie commune pour reconnaître ceux qui vous ont aidé, et rend plus facile pour les autres visiteurs de trouver plus tard la résolution.

    mercredi 13 juillet 2016 08:48
    Modérateur
  • Bonjour,

    Je vous remercie de votre retour. Cependant, le thread cité ne résout pas mon problème.

    En effet, si je ne me trompe pas deux points sont abordés dans le thread :

    - MARS : J'ai oublié de me préciser dans mon précédent post mais j'ai déjà testé de l'activer/désactiver et ça ne change rien aux erreurs.

    - Concurrence sur la méthode Enlist, avec comme solution proposée "synchroniser les connexion" mais dans ce cas ne perd-t-on pas l'intérêt du multrithreading ? De plus, j'ai tenté d'ajouter le tag Enlist=False dans ma chaine de connexion afin qu'ils ne soient pas automatiques. Et d'appeler la méthode EnlistTransaction juste après la méthode Open, le tout dans un lock. Mais j'ai toujours l'erreur du DataReader (même si elle est plus rare), qui lui reste hors lock car sinon je n'ai aucun intérêt à faire du multithread.

    Cordialement,

    Emmanuelle L.

    mercredi 13 juillet 2016 09:54