ダッパーを使用してモデルデータのデータベースを照会する

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

このモデルには、2つのオブジェクトのリストが含まれています。

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クラスには1つまたは複数のタグが関連付けられ、1つまたは複数の業界が関連付けられます。

私はデータベースからのデータでこのモデルを埋める方法が不思議です。私の最初の本能は、複数のクエリが必要だったということでした。オブジェクト情報を取得する1回の呼び出し、オブジェクトに関連付けられたタグを取得するDB呼び出し、およびオブジェクトに関連付けられたすべてのインダストリー情報を取得するための3回目のDB呼び出し。これを行うより簡単な、またはよりクリーンな方法がありますか?この場合、パフォーマンスはかなり悪いと感じています。

受け入れられた回答

あなたは間違って何もしていない、それはAPIが設計された方法ではない。すべてのQuery APIは、 常にデータベース行ごとにオブジェクトを返します。

だから、これは多くの - >一方向ではうまくいくが、1 - >多くのマルチマップではうまくいかない。

ここに2つの問題があります:

  1. あなたのクエリで動作するビルトインマッパーを導入すれば、重複データを "破棄"することが予想されます。 (連絡先。*はあなたの質問に複製されています)

  2. 1対多対で動作するように設計すれば、ある種のアイデンティティマップが必要になります。複雑さが増します。


たとえば、限られた数のレコードをプルする必要がある場合に効率的なこのクエリを考えてみましょう。何百万ものレコードをプッシュすると、ストリームが必要になり、すべてをメモリにロードすることができないため、

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

これはちょっと複雑で、注意が必要です。私はこれをコアに含める方に傾いていません。


人気のある回答

FYI - 次のようにしてSamの答えが得られました:

まず、 "Extensions.cs"というクラスファイルを追加しました。私は2つの場所で "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は合法ですか? はい、理由を学ぶ