Запрос базы данных для данных модели с использованием dapper

c# dapper database sql sql-server

Вопрос

Я использую dapper .net как ORM для проекта, над которым я сейчас работаю, и у меня есть вопрос о запросе базы данных для информации о модели. Например, если у меня есть модель, которая выглядит так:

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 может быть один или несколько связанных тегов и одна или несколько связанных с ними отраслей.

Мне любопытно, как заполнить эту модель данными из базы данных. Мой первоначальный инстинкт заключался в том, что это потребует нескольких запросов. Один вызов, чтобы получить информацию об объекте, вызов БД, чтобы получить теги, связанные с объектом, и третий вызов БД, чтобы получить всю информацию о промышленности, связанную с объектом. Есть ли более простой или более чистый способ сделать это? Я чувствую, что в этом случае производительность будет довольно бедной.

Принятый ответ

Вы ничего не делаете неправильно, это просто не так, как был разработан API. Все API Query всегда возвращают объект на строку базы данных.

Таким образом, это хорошо работает на многих -> одном направлении, но менее хорошо для одного -> много мульти-карт.

Здесь есть 2 вопроса:

  1. Если мы представим встроенный картограф, который будет работать с вашим запросом, мы ожидаем «сбросить» повторяющиеся данные. (В вашем запросе дублируется *. *)

  2. Если мы разработаем его для работы с одной -> многими парами, нам понадобится какая-то идентификационная карта. Это добавляет сложности.


Возьмем, к примеру, этот запрос, который эффективен, если вам просто нужно вытащить ограниченное количество записей, если вы нажмете это на миллион, получится сложнее, потому что вам нужно потоковаться и не может загружать все в память:

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

Поскольку это немного сложно и сложно, с оговорками. Я не склоняюсь к тому, чтобы включить это в ядро.


Популярные ответы

FYI - Я получил ответ Сэма, сделав следующее:

Во-первых, я добавил файл класса под названием «Extensions.cs». Мне пришлось изменить ключевое слово «this» на «читатель» в двух местах:

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
Является ли этот КБ законным? Да, узнайте, почему
Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему