如何使用Dapper Dot Net将数据库结果映射到字典对象?

82

如果我有一个简单的查询,比如:

string sql = "SELECT UniqueString, ID  FROM Table";

我希望将它映射到类似于字典对象的内容中:

and I want to map it to a dictionary object such as:

Dictionary<string, int> myDictionary = new Dictionary<string, int>();      

我该如何使用Dapper实现这个功能?

我猜应该是这样的:

myDictionary = conn.Query<string, int>(sql, new {  }).ToDictionary();

但是无法找出正确的语法。

8个回答

136

已经展示了各种方法;个人建议只使用非通用API:

var dict = conn.Query(sql, args).ToDictionary(
    row => (string)row.UniqueString,
    row => (int)row.Id);

2
我甚至没有意识到在查询方法中可以省略返回类型。 - jpshook
4
请注意,返回字段的属性名称区分大小写。在Oracle中,返回给我的字段都是大写的,例如row.UniqueString在Oracle中返回为row.UNIQUESTRING。 - Colin Pear
3
@Colin,因为其他关系型数据库管理系统允许区分大小写的名称(例如.foo、.FOO和.Foo都是不同的),所以我不确定我能在这方面做出什么改变,除了说“接受您的关系型数据库管理系统返回的结果”。请期待更新。 - Marc Gravell
表现不错,但这种解决方案使用了动态对象,如果有可能我会避免使用。请查看Allen.Cais的解决方案,不需要使用动态对象:https://dev59.com/BWUq5IYBdhLWcg3wHcz5#51815558 - andreas
@andreas 无论哪种方式,它都是同一个对象。它恰好实现了动态工作所需的接口这一事实是分开的。 - Marc Gravell

39
string strSql = "SELECT DISTINCT TableID AS [Key],TableName AS [Value] FROM dbo.TS_TStuctMaster";
Dictionary<string,string> dicts = sqlConnection.Query<KeyValuePair<string,string>>(strSql).ToDictionary(pair => pair.Key, pair => pair.Value);

您可以使用别名和强类型。

别名是与KeyValuePair类型键和值属性相匹配的关键点。

它在强类型下工作并运行良好。

我不喜欢动态类型。在某些情况下会带来灾难性后果。此外,装箱和拆箱会导致性能损失。


1
谢谢,这是最短、最简单的解决方案,同时仍然使用强类型。我猜测那个使用动态类型的被接受的答案运行速度要慢得多。 - andreas

23

如果你正在使用 > .net 4.7 或 netstandard2,你可以使用值元组。代码简洁明了,没有使用动态。

var sql = "SELECT UniqueString, Id  FROM Table";
var dict = conn.Query<(string UniqueString, int Id)>(sql)
           .ToDictionary(t => t.UniqueString,t => t.Id);

20

不需要额外的类也可以工作:

var myDictionary = conn.Query<string, int, KeyValuePair<string,int>>(sql, (s,i) => new KeyValuePair<string, int>(s,i))
    .ToDictionary(kv => kv.Key, kv => kv.Value);

注意: 使用Dapper.NET 3.5版本时,调用Query方法需要指定更多的参数,因为.NET 4.0和.NET 4.5版本利用了可选参数

在这种情况下,以下代码应该能够正常工作:

string splitOn = "TheNameOfTheValueColumn";
var myDictionary = conn.Query<string, int, KeyValuePair<string,int>>(sql, (s,i) => new KeyValuePair<string, int>(s,i), null, null, false, splitOn, null, null)
        .ToDictionary(kv => kv.Key, kv => kv.Value);

大多数参数将恢复为默认值,但splitOn是必需的,因为否则它将默认为'value of 'id''。

对于返回两列'ID'和'Description'的查询,splitOn应设置为'Description'。


1
Tim - 你的解决方案完全有效,但出于简单起见,我更喜欢Marc的。 - jpshook
你能解释一下 "Works also without an additional class" 的意思吗?你是在暗示我需要一个类来实现 Marcs solution 吗?两者都没有很好地解释这个问题。 - Liam
1
@Liam:我想我是在提到w.brian的方法。 - Tim Schmelter
啊,好的。谢谢你澄清。 - Liam

14

Dapper还有一个用于ExecuteReader的扩展方法。因此,您也可以这样做:

var sql = "SELECT UniqueString, ID  FROM Table";
var rows = new List<Dictionary<string, int>>();
using (var reader = cn.ExecuteReader(sql)) {
    while (reader.Read()) {
        var dict = new Dictionary<string, int>();
        for (var i = 0; i < reader.FieldCount; i++) {
            dict[reader.GetName(i)] = reader.GetInt32(i);
        }
        rows.Add(dict);
    }
}

采用这种方法时不需要知道列名。而且,如果您不知道数据类型,可以将Dictionary<string,int>更改为Dictionary<string,object>,并将GetInt32(i)更改为GetValue(i)


漂亮的解决方案,正是我正在寻找的。谢谢。 - oividiosCaeremos

8
我不确定你尝试的操作是否可行。如果您定义一个类来映射查询,这将变得更加简单:
public class MyRow
{
    public int Id { get; set; }
    public string UniqueString { get; set; }
}

那么,你只需要这样做:
var sql = "SELECT UniqueString, ID  FROM Table";
var myDictionary = conn.Query<MyRow>(sql).ToDictionary(row => row.UniqueString, row => row.Id);

3

对于在运行时不知道其结构的表格

    using var db = new SqlConnection(_connectionString);
    var sql = $"Select * From {tableName} Order By {primaryKey}";

    var result = await db.QueryAsync(sql); 
    return result
        .Cast<IDictionary<string, object>>()
        .Select(it => it.ToDictionary(it => it.Key, it => it.Value));

1

我个人更喜欢使用这种方式(一行代码):

var dict = dapper.Query<(string, int)>(sql).ToDictionary(x => x.Item1, row => row.Item2);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接