높은 트래픽 시나리오에서 Dapper.Net을 사용하는 동안 열린 DataReader 문제 - 이제 해결할 수 있습니까?

asp.net c#-4.0 dapper datareader dbdatareader

문제

다음은 Dapper.Net을 사용하는 DAL의 데이터 호출 중 하나의 예입니다.

    /// <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;
    }

일관된 오류는 다음과 같습니다.

2012-06-20 11 : 42 : 44.8903 | 치명적인 | 이미 닫혀 있어야하는이 명령과 연결된 열려있는 DataReader가 있습니다. 시스템의 System.Data.SqlClient.SqlCommand.ValidateCommand (String 메서드, Boolean 비동기)의 System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute (SqlCommand 명령)에서 System.Data.SqlClient.SqlConnection.ValidateConnectionForExecute (String 메서드, SqlCommand 명령) System.Data.SqlClient.SqlCommand.RunExecuteReader (CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String 메서드, DbAsyncResult result)에서 System.Data.SqlClient.SqlCommand.RunExecuteReader (CommandBehavior cmdBehavior, RunBehavior runBehavior, 부울 returnStream, String 메서드)를 호출합니다. Data.SqlClient.SqlCommand.ExecuteReader (CommandBehavior 비헤이비어, String 메서드) System.Data.SqlClient.SqlCommand.ExecuteDbDataReader (CommandBehavior 비헤이비어) System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader ()
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 MyApp.DAL.DapperORM.SqlMapper.Query (IDbConnection cnn, String sql, Object 1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable 1 소스)의 1.ctor (IEnumerable 1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable C : \ Projects \ Git \ MyApp \ MyApp.DAL \ MyApp.DAL.MyAppPrimary.Repositories \ Dapper \ SqlMapper.cs : MyApp.DAL의 538 행에서 IDbTransaction 트랜잭션, 부울 버퍼링, Nullable 1 commandTimeout, Nullable 1 commandType) .Repositories.MemberRepository.AddNotificationEntry (NewsfeedNotification notificationEntry) C : \ Projects \ Git \ MyApp \ MyApp.DAL \ MyApp.DAL.MyAppPrimary.Repositories \ MemberRepositories \ MemberRepository.cs : line 465 2012-06-20 11 : 42 : 45.2491 치명적입니다. 독자가 닫혀있을 때 Read를 호출하지 못했습니다. System.Data.SqlClient.SqlDataReader.ReadInternal (부울 setTimeout)
MyApp.DAL.DapperORM.SqlMapper.d에서 System.Data.SqlClient.SqlDataReader.Read ()
1.MoveNext() in C:\Projects\Git\MyApp\MyApp.DAL\MyApp.DAL.MyAppPrimary.Repositories\Dapper\SqlMapper.cs:line 597 at System.Collections.Generic.List 1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable IEnumerable 1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable MyApp.DAL.DapperORM.SqlMapper의 1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable 1 소스). C : \ Projects \ Git \ MyApp \ MyApp.DAL \ MyApp.DAL.MyAppPrimary.Repositories \에서 쿼리 [T] (IDbConnection cnn, String sql, Object param, IDbTransaction 트랜잭션, 부울 버퍼링, Nullable 1 commandTimeout, Nullable 1 commandType) C : \ Projects \ Git \에있는 MyApp.DAL.DapperORM.SqlMapper.Query (IDbConnection cnn, String sql, Object param, IDbTransaction 트랜잭션, 부울 버퍼링, Nullable 1 commandTimeout, Nullable 1 commandType)에서 538 번째 줄 Dapper \ SqlMapper.cs : MyApp \ MyApp.DAL \ MyApp.DAL.MyAppPrimary.Repositories \ Dapper \ SqlMapper.cs : C : \ Projects \ Git \ MyApp \ MyApp.DA의 MyApp.DAL.Repositories.MemberRepository.GetBuddies (Int32 프로필 ID)의 518 줄 L \ MyApp.DAL.MyAppPrimary.Repositories \ MemberRepositories \ MemberRepository.cs : line 271 2012-06-20 11 : 43 : 01.2392 | 치명적 | 시퀀스에 요소가 없습니다. C : \ Projects \ Git \ MyApp \ MyApp.DAL \ MyApp.DAL.MyAppPrimary의 MyApp.DAL.Repositories.MemberRepository.GetNickname (Int32 프로필 ID)의 System.Linq.Enumerable.First [TSource] (IEnumerable`1 소스) .Repositories \ MemberRepositories \ MemberRepository.cs : 337 행

처음에 나는 using {...} 그 내부로 리턴 값을 가져와 using 블록 밖으로 이동 시켰지만 여전히 같은 문제를 겪고있었습니다.

이것은 트래픽이 많은 응용 프로그램이므로 테스트를 진행할 때까지는이 문제가 실제로 발생하지 않았습니다.

Dapper를 사용하여 DataReader를 관리하기 위해 여기에서 수행해야하는 다른 작업이 있습니까?

----- 업데이트 -----

나는 이것을 더 일찍 게시 했어야하는데, 지금 이것을 추가해야한다.

Dapper.Net의 581 행에는 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;
                }

            }
        }

... 중첩에 거기를 참조 using 코드? 나는 yield return (T)next; 으로 생각해 본다 yield return (T)next; 중첩 내부 동안 내부 코드를 using 해당하는 경우 문제를 야기한다.

문제는 적당한 트래픽으로 Dapper는 정상적으로 작동하는 것 같습니다. 그러나 초당 약 1000 건의 요청을 처리하는 시스템에서는 문제가 발생합니다.

나는 이것이 Dapper 개발자를위한 FYI의 더 많은 부분이라고 생각하고 그들이 이것을 해결할 수 있는지 궁금해한다.

(그리고 나는 코드에서 DapperORM이라는 이름을 잃어 버렸음을 알았습니다 - 그것은 ORM이 아닙니다)

수락 된 답변

Entity Framework를 사용하여 클래스를 생성하고 DAL 액세스를위한 다른 리포지토리를 만들었습니다. Dapper를 사용하는 대신 Entity Framework를 사용하기위한 액세스 코드를 간단히 다시 작성했습니다. EF 연결 문자열과 다른 것은 없으며 내 using 문에서 EF 데이터베이스 컨텍스트를 using 합니다.

그것은 모두 잘 작동합니다.

내가 읽은 것에서 Dapper는 꽤 빠르다. 그래서 나는 처음에 DAL에 이것을 선택했다. 그러나 고주파 트랜잭션 환경에는 한계가있는 것 같습니다. 어쩌면 Dapper 팀이 뭔가를 놓쳤거나 잘못 구현 한 경우에 대비하여이를 분명히 할 수 있습니다.


인기 답변

데이터 배열의 첫 번째 행만 읽으 므로 두 개 이상의 행이 있으면 닫히지 않습니다.



아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.