객체 계층 구조를 만드는 다중 매퍼

dapper multi-mapping

문제

나는 문서화 된 게시물 / 사용자 예제 처럼 많이 느껴졌지만 조금씩 다르며 나에게 효과적이지 않은 것처럼 보였으므로 조금만 놀아 봤다.

다음 간단한 설정을 가정합니다 (연락처에 전화 번호가 여러 개인 경우).

SELECT *
FROM Contacts
    LEFT OUTER JOIN Phones ON Phones.ReferenceId=Contacts.ReferenceId
WHERE clientid=1

여러 Phone 객체로 Contact를 반환하는 무언가로 끝내고 싶습니다. 그런 식으로 2 개의 전화가있는 2 개의 연락처가있는 경우 SQL은 4 개의 총 행이 설정된 결과 집합으로 SQL을 조인합니다. 그러면 Dapper는 두 개의 전화가있는 두 개의 연락처 개체를 띄울 것입니다.

다음은 저장 프로 시저의 SQL입니다.

SELECT *
FROM Contacts
    LEFT OUTER JOIN Phones ON Phones.ReferenceId=Contacts.ReferenceId
WHERE clientid=1

나는 이것을 시도했지만 4 튜플로 끝났다. (괜찮 았지만, 내가 바라는 것이 아니라 ... 결과를 다시 정규화해야 함을 의미한다) :

SELECT *
FROM Contacts
    LEFT OUTER JOIN Phones ON Phones.ReferenceId=Contacts.ReferenceId
WHERE clientid=1

(아래) 다른 메서드를 시도 할 때 "형식 'System.Int32'형식의 개체를 'System.Collections.Generic.IEnumerable`1 [전화]'형식으로 캐스팅 할 수 없습니다."

SELECT *
FROM Contacts
    LEFT OUTER JOIN Phones ON Phones.ReferenceId=Contacts.ReferenceId
WHERE clientid=1

내가 뭔가 잘못하고 있는거야? 게시물 / 소유자 예제처럼 보이지만, 부모 대신 자식에서 부모로 이동한다는 점만 다릅니다.

미리 감사드립니다.

수락 된 답변

당신은 아무 잘못도 없다. 그것은 API가 설계된 방식이 아니다. 모든 Query API는 항상 데이터베이스 행당 오브젝트를 리턴합니다.

따라서 이것은 여러 방향에서 잘 작동하지만, 하나의 맵에서 많은 맵을 사용하는 것은 좋지 않습니다.

여기에 2 가지 문제가 있습니다.

  1. 쿼리와 함께 작동하는 내장 매퍼 (mapper)를 도입하면 중복 데이터를 "폐기"해야합니다. (연락처. *는 쿼리에서 중복됩니다.)

  2. 우리가 one -> many pair로 작업하도록 설계한다면, 일종의 identity map이 필요할 것입니다. 복잡성이 추가됩니다.


예를 들어 제한된 수의 레코드를 가져와야하는 경우에 효율적으로 사용할 수있는이 쿼리를 생각해보십시오.이 작업을 100 만 건으로 밀어 넣는다면 더 까다로워 질 수 있습니다. 스트리밍해야하고 모든 것을 메모리에로드 할 수 없기 때문입니다.

var mapped = cnn.QueryMultiple(sql)
   .Map<Contact,Phone, int>
    (
       contact => contact.ContactID, 
       phone => phone.ContactID,
       (contact, phones) => { contact.Phones = phones };  
    );

GridReader 를 확장하여 다시 매핑 할 수 있습니다.

var mapped = cnn.QueryMultiple(sql)
   .Map<Contact,Phone, int>
    (
       contact => contact.ContactID, 
       phone => phone.ContactID,
       (contact, phones) => { contact.Phones = phones };  
    );

GridReader와 매퍼 (mapper)를 확장한다고 가정합니다.

var mapped = cnn.QueryMultiple(sql)
   .Map<Contact,Phone, int>
    (
       contact => contact.ContactID, 
       phone => phone.ContactID,
       (contact, phones) => { contact.Phones = phones };  
    );

이것은 약간 까다롭고 복잡하기 때문에주의해야합니다. 나는 이것을 핵심에 포함시키는 방향으로 기대지 않는다.


인기 답변

참고 - 다음과 같이 Sam의 대답을 얻었습니다.

먼저 "Extensions.cs"라는 클래스 파일을 추가했습니다. 두 곳에서 "this"키워드를 "reader"로 변경해야했습니다.

public IEnumerable<Contact> GetContactsAndPhoneNumbers()
{
    var sql = @"
SELECT * FROM Contacts WHERE clientid=1
SELECT * FROM Phone where ContactId in (select ContactId FROM Contacts WHERE clientid=1)";

    using (var connection = GetOpenConnection())
    {
        var mapped = connection.QueryMultiple(sql)    
            .Map<Contact,Phone, int>     (        
            contact => contact.ContactID,        
            phone => phone.ContactID,
            (contact, phones) => { contact.Phones = phones; }      
        ); 
        return mapped;
    }
}

둘째, 마지막 매개 변수를 수정하여 다음 메서드를 추가했습니다.

public IEnumerable<Contact> GetContactsAndPhoneNumbers()
{
    var sql = @"
SELECT * FROM Contacts WHERE clientid=1
SELECT * FROM Phone where ContactId in (select ContactId FROM Contacts WHERE clientid=1)";

    using (var connection = GetOpenConnection())
    {
        var mapped = connection.QueryMultiple(sql)    
            .Map<Contact,Phone, int>     (        
            contact => contact.ContactID,        
            phone => phone.ContactID,
            (contact, phones) => { contact.Phones = phones; }      
        ); 
        return mapped;
    }
}



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