使用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类可以关联一个或多个标记,并关联一个或多个行业。

我很好奇如何使用数据库中的数据填充此模型。我最初的直觉是需要多次查询。一次调用获取Object信息,一次调用DB调用以获取与该对象关联的标记,另一次调用以获取与该对象关联的所有Industry信息。有更简单,更清洁的方法吗?在这种情况下,我觉得表现会很糟糕。

一般承认的答案

你没有做错任何事,这不是API的设计方式。所有Query API将始终为每个数据库行返回一个对象。

因此,这在很多 - >一个方向上运行良好,但对于一个方向不太好 - >多个多地图。

这里有两个问题:

  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并使用映射器:

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合法吗? 是的,了解原因