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