데이터베이스를 사용하여 모델 데이터 쿼리하기

c# dapper database sql sql-server

문제

나는 현재 작업중인 프로젝트에 대해 ORM으로 dapper .net을 사용하고 있으며 데이터베이스에 모델 정보를 쿼리하는 방법에 대한 질문이 있습니다. 예를 들어 다음과 같은 모델이있는 경우 :

public class Object
{
    public string Title { get; set; }
    public string Body { get; set; }
    public List<Tag> TagList{ get; set; }
    public List<Industry> IndustryList{ get; set; } 
}

이 모델에는 두 개의 객체 목록이 있습니다.

public class Tag
{
    public int TagID { get; set; }
    public string Name { get; set; }
}

public class Industry
{
    public int IndustryID { get; set; }
    public string Name { get; set; }
}

보시다시피 Object 클래스에는 하나 이상의 태그가 연결될 수 있으며 하나 이상의 산업이 연결될 수 있습니다.

이 모델을 데이터베이스의 데이터로 채우는 방법이 궁금합니다. 필자의 본능은 다중 쿼리가 필요하다는 것이 었습니다. 객체 정보를 얻기위한 하나의 호출, 객체와 연관된 태그를 얻기위한 DB 호출, 그리고 객체와 관련된 모든 산업 정보를 얻기위한 세 번째 DB 호출. 이 작업을 쉽게 수행 할 수있는 방법이 있습니까? 이 경우 성능이 상당히 떨어질 것 같아요.

수락 된 답변

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

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

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

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

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


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

var sql = "set nocount on
DECLARE @t TABLE(ContactID int,  ContactName nvarchar(100))
INSERT @t
SELECT *
FROM Contacts
WHERE clientid=1
set nocount off 
SELECT * FROM @t 
SELECT * FROM Phone where ContactId in (select t.ContactId from @t t)"

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

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

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

public static IEnumerable<TFirst> Map<TFirst, TSecond, TKey>
    (
    this GridReader reader,
    Func<TFirst, TKey> firstKey, 
    Func<TSecond, TKey> secondKey, 
    Action<TFirst, IEnumerable<TSecond>> addChildren
    )
{
    var first = reader.Read<TFirst>().ToList();
    var childMap = reader
        .Read<TSecond>()
        .GroupBy(s => secondKey(s))
        .ToDictionary(g => g.Key, g => g.AsEnumerable());

    foreach (var item in first)
    {
        IEnumerable<TSecond> children;
        if(childMap.TryGetValue(firstKey(item), out children))
        {
            addChildren(item,children);
        }
    }

    return first;
}

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


인기 답변

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

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

using System;
using System.Collections.Generic;
using System.Linq;
using Dapper;

namespace TestMySQL.Helpers
{
    public static class Extensions
    {
        public static IEnumerable<TFirst> Map<TFirst, TSecond, TKey>
            (
            this Dapper.SqlMapper.GridReader reader,
            Func<TFirst, TKey> firstKey,
            Func<TSecond, TKey> secondKey,
            Action<TFirst, IEnumerable<TSecond>> addChildren
            )
        {
            var first = reader.Read<TFirst>().ToList();
            var childMap = reader
                .Read<TSecond>()
                .GroupBy(s => secondKey(s))
                .ToDictionary(g => g.Key, g => g.AsEnumerable());

            foreach (var item in first)
            {
                IEnumerable<TSecond> children;
                if (childMap.TryGetValue(firstKey(item), out children))
                {
                    addChildren(item, children);
                }
            }

            return first;
        }
    }
}

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

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는 합법적입니까? 예, 이유를 알아보십시오.