Interrogation de la base de données pour les données de modèle à l'aide de dapper

c# dapper database sql sql-server

Question

J'utilise dapper .net comme ORM pour un projet sur lequel je travaille actuellement et j'ai une question sur l'interrogation de la base de données pour obtenir des informations sur les modèles. Par exemple, si j'ai un modèle qui ressemble à ceci:

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

Ce modèle contient une liste de deux objets:

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

Comme vous pouvez le voir, la classe Object peut avoir un ou plusieurs tags associés et une ou plusieurs industries associées.

Je suis curieux de savoir comment remplir ce modèle avec les données de la base de données. Mon instinct initial était qu'il nécessiterait plusieurs requêtes. Un appel pour obtenir les informations d'objet, un appel de base de données pour obtenir les balises associées à l'objet, et un troisième appel de base de données pour obtenir toutes les informations sectorielles associées à l'objet. Y a-t-il un moyen plus facile ou plus propre de le faire? Je pense que la performance serait plutôt médiocre dans ce cas.

Réponse acceptée

Vous ne faites rien de mal, ce n'est pas la façon dont l'API a été conçue. Toutes les API de Query renverront toujours un objet par ligne de base de données.

Donc, cela fonctionne bien sur les nombreuses -> une directions, mais moins bien pour celui -> beaucoup de multi-cartes.

Il y a 2 problèmes ici:

  1. Si nous introduisons un mappeur intégré qui fonctionne avec votre requête, nous sommes supposés "ignorer" les données en double. (Contacts. * Est dupliqué dans votre requête)

  2. Si nous le concevons pour fonctionner avec une paire unique, nous aurons besoin d'une sorte de carte d'identité. Ce qui ajoute de la complexité.


Prenons par exemple cette requête qui est efficace si vous avez juste besoin d'extraire un nombre limité d'enregistrements, si vous poussez ce nombre jusqu'à un million d'objets, devenez plus compliqué, car vous devez diffuser et ne pouvez pas tout charger en mémoire:

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)"

Ce que vous pouvez faire est d'étendre le GridReader pour permettre le remappage:

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

En supposant que vous étendez votre GridReader et avec un mappeur:

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

Puisque c'est un peu difficile et complexe, avec des mises en garde. Je ne penche pas pour inclure cela dans le noyau.


Réponse populaire

FYI - J'ai obtenu la réponse de Sam en faisant ce qui suit:

Tout d'abord, j'ai ajouté un fichier de classe appelé "Extensions.cs". J'ai dû remplacer le mot-clé "this" par "reader" à deux endroits:

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

Deuxièmement, j'ai ajouté la méthode suivante, en modifiant le dernier paramètre:

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


Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi