Problèmes Open DataReader lors de l'utilisation de Dapper.Net dans un scénario à fort trafic - maintenant à résoudre?

asp.net c#-4.0 dapper datareader dbdatareader

Question

Voici un exemple d'un de nos appels de données dans notre DAL à l'aide de Dapper.Net:

    /// <summary>
    /// Handles db connectivity as Dapper assumes an existing connection for all functions
    /// Since the app uses three databases, pass in the connection string for the required db.
    /// </summary>
    /// <returns></returns>
    protected static IDbConnection OpenConnection(string connectionStringName)
    {
        try
        {
            connection = new SqlConnection(WebConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString);
            //connection = SqlMapperUtil.GetOpenConnection(connectionStringName);       // if we want to use the Dapper utility methods
            //connection = new SqlConnection(connectionString);
            connection.Open();
            return connection;
        }
        catch (Exception ex)
        {
            ErrorLogging.Instance.Fatal(ex);        // uses singleton for logging
            return null;
        }
    }


    public string GetNickname(int profileID)
    {
        string nickname = string.Empty;

        using (IDbConnection connection = OpenConnection("PrimaryDBConnectionString"))
        {
            try
            {
                var sp_nickname = connection.Query<string>("sq_mobile_nickname_get_by_profileid", new { profileID = profileID }, commandType: CommandType.StoredProcedure);
                nickname = sp_nickname.First<string>();
            }
            catch (Exception ex)
            {
                ErrorLogging.Instance.Fatal(ex);
                return null;
            }
        }

        return nickname;
    }

Les erreurs constantes que nous voyons sont les suivantes:

2012-06-20 11: 42: 44.8903 | Fatal | Il existe déjà un DataReader ouvert associé à cette commande qui doit être fermé en premier. | at System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute (commande SqlCommand) à System.Data.SqlClient.SqlConnection.ValidateConnectionForExecute (méthode String, commande SqlCommand) sur System.Data.SqlClient.SqlCommand.ValidateCommand (méthode String, asynchrone booléenne) sur System .Data.SqlClient.SqlCommand.RunExecuteReader (CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, méthode String, résultat DbAsyncResult) à System.Data.SqlClient.SqlCommand.RunExecuteReader (CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, méthode String) sur System. Data.SqlClient.SqlCommand.ExecuteReader (comportement CommandBehavior, méthode String) sur System.Data.SqlClient.SqlCommand.ExecuteDbDataReader (comportement CommandBehavior) sur System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader ()
at MyApp.DAL.DapperORM.SqlMapper.d_ 13 1.MoveNext() in C:\Projects\Git\MyApp\MyApp.DAL\MyApp.DAL.MyAppPrimary.Repositories\Dapper\SqlMapper.cs:line 581 at System.Collections.Generic.List 1..ctor ( 1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable IEnumerable 1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable source 1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable 1) sur MyApp.DAL.DapperORM.SqlMapper.Query [T] (IDbConnection cnn, String sql, Object param, transaction IDbTransaction, tampon booléen, Nullable 1 commandTimeout, Nullable 1) dans C: \ Projects \ Git \ MyApp \ MyApp.DAL \ MyApp.DAL.MyAppPrimary.Repositories \ Dapper \ SqlMapper.cs: ligne 538 à MyApp.DAL .Repositories.MemberRepository.AddNotificationEntry (NewsfeedNotification notificationEntry) dans C: \ Projects \ Git \ MyApp \ MyApp.DAL \ MyApp.DAL.MyAppPrimary.Repositories \ MemberRepositories \ MemberRepository.cs: ligne 465 2012-06-20 11: 42: 45.2491 | Fatal | Tentative non valide d'appeler Lire lorsque le lecteur est fermé. | at System.Data.SqlClient.SqlDataReader.ReadInternal (Boolean setTimeout)
at System.Data.SqlClient.SqlDataReader.Read () à MyApp.DAL.DapperORM.SqlMapper.d
_13 1.MoveNext() in C:\Projects\Git\MyApp\MyApp.DAL\MyApp.DAL.MyAppPrimary.Repositories\Dapper\SqlMapper.cs:line 597 at System.Collections.Generic.List 1..ctor ( 1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable IEnumerable 1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable source 1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable 1) à MyApp.DAL.DapperORM.SqlMapper. Requête [T] (cnn IDbConnection, sql String, paramètre Object, transaction IDbTransaction, tampon booléen, Nullable 1 commandTimeout, Nullable 1) dans C: \ Projects \ Git \ MyApp \ MyApp.DAL \ MyApp.DAL.MyAppPrimary.Repositories \ Dapper \ SqlMapper.cs: ligne 538 sur MyApp.DAL.DapperORM.SqlMapper.Query (IDbConnection cnn, chaîne SQL, paramètre objet, transaction IDbTransaction, tampon booléen, Nullable 1 commandTimeout, Nullable 1) dans C: \ Projects \ Git \ MyApp \ MyApp.DAL \ MyApp.DAL.MyAppPrimary.Repositories \ Dapper \ SqlMapper.cs: ligne 518 à MyApp.DAL.Repositories.MemberRepository.GetBuddies (ID de profil Int32) dans C: \ Projects \ Git \ MyApp \ MyApp.DA L \ MyApp.DAL.MyAppPrimary.Repositories \ MemberRepositories \ MemberRepository.cs: ligne 271 2012-06-20 11: 43: 01.2392 | Fatal | Séquence ne contient aucun élément | à System.Linq.Enumerable.First [TSource] (source IEnumerable`1) à MyApp.DAL.Repositories.MemberRepository.GetNickname (ID de profil Int32) dans C: \ Projects \ Git \ MyApp \ MyApp.DAL \ MyApp.DAL.MyAppPrimary .Repositories \ MemberRepositories \ MemberRepository.cs: ligne 337

Au départ, j'avais les retours à l'intérieur de using {...} et les déplaçais en dehors du bloc using , mais rencontrais toujours les mêmes problèmes.

Ceci est une application à fort trafic, donc en testant ce problème ne s'est pas vraiment manifesté avant notre mise en ligne.

Y a-t-il autre chose à faire ici pour la gestion de DataReader avec Dapper?

----- METTRE À JOUR -----

J'aurais dû poster cela plus tôt, mais en ajoutant ceci maintenant.

La ligne 581 du Dapper.Net contient le code ExecuteReader() :

   private static IEnumerable<T> QueryInternal<T>(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType)
    {
        var identity = new Identity(sql, commandType, cnn, typeof(T), param == null ? null : param.GetType(), null);
        var info = GetCacheInfo(identity);

        using (var cmd = SetupCommand(cnn, transaction, sql, info.ParamReader, param, commandTimeout, commandType))
        {
            using (var reader = cmd.ExecuteReader())
            {
                Func<Func<IDataReader, object>> cacheDeserializer =  () =>
                {
                    info.Deserializer = GetDeserializer(typeof(T), reader, 0, -1, false);
                    SetQueryCache(identity, info);
                    return info.Deserializer;
                };

                if (info.Deserializer == null)
                {
                    cacheDeserializer();
                }

                var deserializer = info.Deserializer;

                while (reader.Read())
                {
                    object next;
                    try
                    {
                        next = deserializer(reader);
                    }
                    catch (DataException)
                    {
                        // give it another shot, in case the underlying schema changed
                        deserializer = cacheDeserializer();
                        next = deserializer(reader);
                    }
                    yield return (T)next;
                }

            }
        }

... voir là dans le imbriqué en using le code? Je me demande si en raison du yield return (T)next; code dans le temps, à l'intérieur de l' using imbriquée, si cela cause un problème.

Le fait est que, avec un trafic modéré, Dapper semble fonctionner correctement. Cependant, dans un système contenant environ 1 000 requêtes par seconde, il semble se déclencher.

Je suppose que c'est plus un FYI pour les développeurs de Dapper, et je me demande s'ils pourraient résoudre ce problème.

(et je me rends compte que DapperORM m'a manqué dans le code - ce n'est pas un ORM)

Réponse acceptée

J'ai utilisé Entity Framework pour générer mes classes, et j'ai donc créé un référentiel différent pour l'accès DAL - plutôt que d'utiliser Dapper, j'ai simplement réécrit le code d'accès pour utiliser Entity Framework. Rien de différent des chaînes de connexion EF et l'utilisation du contexte de la base de données EF dans mes instructions using .

Tout fonctionne très bien.

D'après ce que j'ai lu, Dapper est assez rapide, c'est pourquoi j'ai initialement choisi ceci pour mon DAL. Cependant, il semble qu’il ait ses limites dans un environnement de transactions à haute fréquence. Peut-être que l'équipe Dapper peut clarifier cela si je manque quelque chose ou si quelque chose a été mal implémenté.


Réponse populaire

Vous ne lisez que la première ligne du lecteur de données, il ne sera donc jamais fermé s'il y a plusieurs lignes.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi