Dapper에서 버퍼링되지 않은 쿼리와의 연결 관리

.net ado.net c# dapper micro-orm

문제

최근에 Dapper를 사용하기 시작했습니다. 모든 것이 멋지고 쉽지만, 계속 혼란스럽게하는 한 가지가 있습니다. 연결 관리.

문서에 따라 :

Dapper는 연결 라이프 사이클을 관리하지 않습니다. 연결이 열렸으며 열거 형 (MARS가 활성화되어 있지 않은 경우)이없는 것으로 가정합니다.

이 점을 고려하여 필자는 저장소 방법을 구현하기 시작했다.

using (var db = new SqliteConnection(connectionString)) {
    // call Dapper methods here
}

그런 다음 많은 수의 레코드가있는 테이블을 발견 했으므로 buffered: falseQuery<> 메서드에 전달하여 IEnumerable<T> 을 반환했지만 프런트 엔드에서 열거 형을 열거 시작했을 때 예외가 발생했습니다 연결이 닫히고 처분되었다고 말하면서 나는 선행 블록을 사용하여 통화를 포장하고 있기 때문에 예상된다.

질문 : 이것을 해결하는 가장 좋은 방법은?
부차적 인 질문 : 연결을 선호하는 방법으로 관리하고있는 것입니까?

수락 된 답변

나는이 저장소 패턴을 제공 할 것이다.

public class Repository
{
    private readonly string _connectionString;

    public Repository(string connectionString)
    {
        _connectionString = connectionString;
    }

    protected T GetConnection<T>(Func<IDbConnection, T> getData)
    {
        using (var connection = new SqlConnection(_connectionString))
        {
            connection.Open();
            return getData(connection);
        }
    }

    protected TResult GetConnection<TRead, TResult>(Func<IDbConnection, TRead> getData, Func<TRead, TResult> process)
    {
        using (var connection = new SqlConnection(_connectionString))
        {
            connection.Open();
            var data = getData(connection);
            return process(data);
        }
    }
}

버퍼링 된 쿼리의 경우 GetConnection 메서드의 첫 번째 오버로드를 사용하고 버퍼가 아닌 경우 두 번째를 사용하여 데이터 처리를위한 콜백을 지정합니다.

public class MyRepository : Repository
{
    public MyRepository(string connectionString) : base(connectionString)
    {
    }

    public IEnumerable<MyMapObject> GetData()
    {
        return GetConnection(c => c.Query<MyMapObject>(query));
    }

    public IEnumerable<ResultObject> GetLotsOfData(Func<IEnumerable<MyMapObject>, IEnumerable<ResultObject>> process)
    {
        return GetConnection(c => c.Query<MyMapObject>(query, buffered: false), process);
    }
}

아주 기본적인 사용법 :

static void Main(string[] args)
{
    var repository = new MyRepository(connectionString);
    var data = repository.GetLotsOfData(ProcessData);
}

public static IEnumerable<ResultObject> ProcessData(IEnumerable<MyMapObject> data)
{
    foreach (var record in data)
    {
        var result = new ResultObject();
        //do some work...
        yield return result;
    }
}

그러나이 경우에는 연결이 너무 오랫동안 열릴 수 있습니다 ...


인기 답변

@Sergio, 대단해! 좋은 패턴을 가져 주셔서 감사합니다. Dapper의 비동기 메소드와 함께 사용할 수 있도록 비동기로 약간 수정했습니다. 내 전체 요청 체인을 비동기로 만들고 컨트롤러에서 DB로 되돌려줍니다! 화려한!

public abstract class BaseRepository
{
    private readonly string _ConnectionString;

    protected BaseRepository(string connectionString)
    {
        _ConnectionString = connectionString;
    }

    // use for buffered queries
    protected async Task<T> WithConnection<T>(Func<IDbConnection, Task<T>> getData)
    {
        try
        {
            using (var connection = new SqlConnection(_ConnectionString))
            {
                await connection.OpenAsync();
                return await getData(connection);
            }
        }
        catch (TimeoutException ex)
        {
            throw new Exception(String.Format("{0}.WithConnection() experienced a SQL timeout", GetType().FullName), ex);
        }
        catch (SqlException ex)
        {
            throw new Exception(String.Format("{0}.WithConnection() experienced a SQL exception (not a timeout)", GetType().FullName), ex);
        }
    }

    // use for non-buffeed queries
    protected async Task<TResult> WithConnection<TRead, TResult>(Func<IDbConnection, Task<TRead>> getData, Func<TRead, Task<TResult>> process)
    {
        try
        {
            using (var connection = new SqlConnection(_ConnectionString))
            {
                await connection.OpenAsync();
                var data = await getData(connection);
                return await process(data);
            }
        }
        catch (TimeoutException ex)
        {
            throw new Exception(String.Format("{0}.WithConnection() experienced a SQL timeout", GetType().FullName), ex);
        }
        catch (SqlException ex)
        {
            throw new Exception(String.Format("{0}.WithConnection() experienced a SQL exception (not a timeout)", GetType().FullName), ex);
        }
    }
}

다음과 같이 Dapper와 함께 사용하십시오.

public class PersonRepository : BaseRepository
{
    public PersonRepository(string connectionString): base (connectionString) { }

    // Assumes you have a Person table in your DB that 
    // aligns with a Person POCO model.
    //
    // Assumes you have an existing SQL sproc in your DB 
    // with @Id UNIQUEIDENTIFIER as a parameter. The sproc 
    // returns rows from the Person table.
    public async Task<Person> GetPersonById(Guid Id)
    {
        return await WithConnection(async c =>
        {
            var p = new DynamicParameters();
            p.Add("Id", Id, DbType.Guid);
            var people = await c.QueryAsync<Person>(sql: "sp_Person_GetById", param: p, commandType: CommandType.StoredProcedure);
            return people.FirstOrDefault();
        });
    }
}


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