81

If I have a simple query such as:

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>();      

How would I do this with Dapper?

I assume it is something like:

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

But can't figure out the proper syntax.

jpshook
  • 4,834
  • 6
  • 36
  • 45

8 Answers8

136

There's various ways already shown; personally I'd just use the non-generic api:

var dict = conn.Query(sql, args).ToDictionary(
    row => (string)row.UniqueString,
    row => (int)row.Id);
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 2
    I didn't even realize you could leave out the return type in the query method. – jpshook Feb 12 '13 at 20:47
  • 3
    FYI. Those property names for the fields being returned are case sensitive. In Oracle the fields being returned to me were in all capital letters so for example row.UniqueString was coming back from Oracle as row.UNIQUESTRING. – Colin Pear May 13 '14 at 18:55
  • 3
    @Colin since other RDBMS allow for case sensitive names (so .foo, .FOO and .Foo are all different) I am not sure I can do much about that, other than to say "expect whatever your RDBMS returns" – Marc Gravell May 13 '14 at 18:57
  • Works well, but this solution uses dynamic objects, which I try to avoid, if possible. Check out Allen.Cais solution, without dynamic objects: https://stackoverflow.com/a/51815558/4249619 – andreas Sep 17 '18 at 12:01
  • @andreas it is the same object either way. The fact that it happens to implement the interfaces necessary for dynamic to work is separate – Marc Gravell Sep 17 '18 at 14:12
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);

You can use aliases and strong types.

Aliases are the key points, which match the attributes of KeyValuePair type Key and Value.

It works under strong typing and runs well.

I don't like dynamic type. It brings disaster in certain situations. Moreover, the boxing and unboxing brings performance loss.

Allen.Cai
  • 501
  • 6
  • 7
  • 1
    Thanks, that's the shortest and easiest solution while still using strong typing. I guess the accepted answer with dynamic typing runs much slower. – andreas Sep 17 '18 at 11:59
23

If you are using > .net 4.7 or netstandard2 you can use value tuples. the code is nice and terse and there is no use of dynamics.

var sql = "SELECT UniqueString, Id  FROM Table";
var dict = conn.Query<(string UniqueString, int Id)>(sql)
           .ToDictionary(t => t.UniqueString,t => t.Id);
herostwist
  • 3,778
  • 1
  • 26
  • 34
20

Works also without an additional class:

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

NOTE: When using Dapper.NET 3.5 version, the Query method that takes the first, second and return types requires you specify more parameters, as the .NET 4.0 and .NET 4.5 versions take advantage of optional arguments.

In this case, the following code should work:

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

Most of the arguments will revert to a default, but splitOn is required, as it will otherwise default to a value of 'id'.

For a query that returns two columns, 'ID' and 'Description', splitOn should be set to 'Description'.

Adam Marshall
  • 3,010
  • 9
  • 42
  • 80
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • 1
    Tim - Your solution is completely valid, but I prefer Marc's for simplicity sake. – jpshook Feb 12 '13 at 20:49
  • Can you explain *Works also without an additional class* are you implying I need a class for [Marcs solution](http://stackoverflow.com/a/14786328/542251)? Neither really explains this – Liam Mar 23 '17 at 10:44
  • 1
    @Liam: i think i was referring to [w.brian's](http://stackoverflow.com/a/14781485/284240) approach. – Tim Schmelter Mar 23 '17 at 10:48
  • Ah OK. Thanks for clarifying – Liam Mar 23 '17 at 10:48
12

Dapper also has an extension method for ExecuteReader. So, you could also do this:

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

This approach works without knowing the column names. Moreover, if you don't know the data types, you could change Dictionary<string,int> to Dictionary<string,object> and GetInt32(i) to GetValue(i).

dalenewman
  • 1,234
  • 14
  • 18
8

I'm not sure if what you're trying to do is possible. If you define a class to map the query to this becomes far more trivial:

public class MyRow
{
    public int Id { get; set; }
    public string UniqueString { get; set; }
}

Then, you would just do this:

var sql = "SELECT UniqueString, ID  FROM Table";
var myDictionary = conn.Query<MyRow>(sql).ToDictionary(row => row.UniqueString, row => row.Id);
w.brian
  • 16,296
  • 14
  • 69
  • 118
3

For the table of which you do not know the structure at runtime

    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));
Liam Kernighan
  • 2,335
  • 1
  • 21
  • 24
0

I personally prefer to use like this (1 liner):

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