Async 및 Await을 사용하여 데이터베이스 호출 중단 (Dapper 사용)

async-await asynchronous c# dapper

문제

Dapper에서 수천 개의 개체를 다시 요청하고 매개 변수 한도 (2100)에 도달하여 청크로로드하기로 결정했습니다.

Async Await를 시도해 볼 수있는 좋은 기회라고 생각했습니다. 처음으로 학교 소년과 같은 실수를 저지른 것은 처음입니다.

중단 점에 도달했지만 모든 것이 반환되지 않습니다. 그것은 오류를 던지 고있는 것이 아닙니다. 모든 것이 블랙홀에 들어가는 것처럼 보입니다!

도와주세요!

이것은 원래의 방법이었습니다. 이제 Async 메서드를 호출합니다.

    public List<MyObject> Get(IEnumerable<int> ids)
    {
        return this.GetMyObjectsAsync(ids).Result.ToList();
    }  //Breakpoint on this final bracket never gets hit

이 메소드를 추가하여 id를 1000의 청크로 분할 한 다음 완료 할 작업을 기다리고 있습니다.

    private async Task<List<MyObject>> GetMyObjectsAsync(IEnumerable<int> ids)
    {
        var subSets = this.Partition(ids, 1000);

        var tasks = subSets.Select(set => GetMyObjectsTask(set.ToArray()));

        //breakpoint on the line below gets hit ...
        var multiLists = await Task.WhenAll(tasks);

        //breakpoint on line below never gets hit ...
        var list = new List<MyObject>();
        foreach (var myobj in multiLists)
        {
            list.AddRange(myobj);   
        }
        return list;
    }

아래 작업이 ...

    private async Task<IEnumerable<MyObject>> GetMyObjectsTask(params int[] ids)
    {
        using (var db = new SqlConnection(this.connectionString))
        {
            //breakpoint on the line below gets hit
            await db.OpenAsync();
            return await db.QueryAsync<MyObject>(@"SELECT Something FROM Somewhere WHERE ID IN @Ids",
            new { ids});
        }
    }

다음의 메소드는 청크에있는 ID 목록을 분할합니다. - 이것은 잘 작동하는 것처럼 보입니다 ...

    private IEnumerable<IEnumerable<T>> Partition<T>(IEnumerable<T> source, int size)
    {
        var partition = new List<T>(size);
        var counter = 0;

        using (var enumerator = source.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                partition.Add(enumerator.Current);
                counter++;
                if (counter % size == 0)
                {
                    yield return partition.ToList();
                    partition.Clear();
                    counter = 0;
                }
            }

            if (counter != 0)
                yield return partition;
        }
    }

수락 된 답변

Task<T>.Result 와 함께 async/await 를 사용하는 것과 같은 교착 상태 상황을 Task<T>.Result .

문제가되는 행은 다음과 같습니다.

return this.GetMyObjectsAsync(ids).Result.ToList();

GetMyObjectsAsync(ids).Result 는 현재 동기화 컨텍스트를 차단합니다. 그러나 GetMyObjectsAsync 는 현재 동기화 컨텍스트의 연속 지점을 예약하는 await 를 사용합니다. 이 방법으로 문제를 볼 수있을 것이라고 확신합니다. 즉, 현재 동기화 컨텍스트가 Task.Result 의해 차단되어 Task.Result .

경우에 따라 작동 할 수있는 하나의 솔루션은 ConfigureAwait(false) 를 사용하는 것입니다. 즉, 연속이 모든 동기화 컨텍스트에서 실행될 수 있음을 의미합니다.

하지만 일반적으로 Task.Result async/await 를 사용하지 않는 것이 가장 좋습니다.


교착 상태가 실제로 발생하는지 여부는 비동기 메서드를 호출 할 때 사용되는 동기화 컨텍스트에 따라 달라집니다. ASP.net, Windows Forms 및 WPF의 경우 교착 상태가 발생하지만 콘솔 애플리케이션에는 해당되지 않습니다. (그의 코멘트 Marc Gravell에게 감사드립니다)


Microsoft에는 비동기 프로그래밍 모범 사례에 대한 좋은 기사가 있습니다. (감사합니다 ken2k)


인기 답변

필자는 매개 변수가 대소 문자를 구분해야한다고 생각합니다.

return await db.QueryAsync<MyObject>(@"SELECT Something FROM Somewhere WHERE ID IN @ids",
            new { ids});

아래 쿼리에서 "@Ids"대신 :

 return await db.QueryAsync<MyObject>(@"SELECT Something FROM Somewhere WHERE ID IN **@Ids**",
            new { ids});

확실하지 않지만 단지 시험해보십시오.



아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow