Linq-to-SQL ToDictionary()

89

如何使用Linq从SQL(2008)正确转换两列为Dictionary(用于缓存)?

目前,我通过循环IQueryable来实现,因为我无法使ToDictionary方法工作。 有什么想法吗? 以下是可行的示例:

var query = from p in db.Table
            select p;

Dictionary<string, string> dic = new Dictionary<string, string>();

foreach (var p in query)
{
    dic.Add(sub.Key, sub.Value);
}

我真正想做的是像这样的事情,但似乎行不通:

var dic = (from p in db.Table
             select new {p.Key, p.Value })
            .ToDictionary<string, string>(p => p.Key);

但是我遇到了这个错误:

无法将'System.Linq.IQueryable<AnonymousType#1>'转换为 'System.Collections.Generic.IEnumerable'

4个回答

131
var dictionary = db
    .Table
    .Select(p => new { p.Key, p.Value })
    .AsEnumerable()
    .ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
;

抱歉,我可能没有表达清楚,我之前尝试过这个,但它会创建一个Dictionary<string,#匿名类型>,而不是Dictionary<string,string>。 - Codewerks
尝试使用 kvp => kvp.Value as string。答案的重点在于 .AsEnumerable() - yfeldblum
谢谢,是的,我想到了需要使用AsEnumerable,但ToDictionary调用仍然无法工作。在SQL中,这两列都是varchars,因此它们返回为字符串,但我无法弄清楚如何将其放入字典中... - Codewerks
我不知道这是否仍然相关,但为什么需要使用AsEnumerable?对我来说,即使没有它也可以工作,但可能Linq已经改变了(已经过去7年)。 - Alexander Derck
1
@AlexanderDerck - 当时,AsEnumerable 是必要的。虽然经过这么长时间,我不太记得具体原因,但可能的解释是 ToDictionaryIEnumerable 的扩展方法,但 Linq-to-SQL 的接口不是 IEnumerable - yfeldblum

19

您仅定义了键名,但还需要包括对应的值:

var dic = (from p in db.Table
             select new {p.Key, p.Value })
            .ToDictionary(p => p.Key, p=> p.Value);

3
错误在于 <string, string> 的部分导致使用了 public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer); 重载方法,而非 public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector); 的重载方法。 - user247702
我认为在 .ToDictionary() 之前仍需要 .AsEnumerable() - AceMark
完美地工作了...终于找到我要的那一个。 - Brian

9

谢谢大家,你们的回答帮助我解决了这个问题,应该是:

var dic = db
        .Table
        .Select(p => new { p.Key, p.Value })
        .AsEnumerable()
        .ToDictionary(k=> k.Key, v => v.Value);

5
你需要使用AsEnumerable()的原因是因为LINQ to SQL不会混合本地和远程(SQL)处理,所以这将导致第一部分在SQL服务器上执行,然后最终的后续部分在本地使用LINQ to Objects执行,这可以使用Dictionarys :) - DamienG
有道理。另外,ToDictionary中的第二个参数需要自己的Func参数,这让我之前很困扰。 - Codewerks

2
为什么要为表中的每个项目创建匿名对象,然后再进行转换呢?
你可以简单地使用以下代码: IDictionary<string, string> dic = db.Table.ToDictionary(row => row.Key, row => row.Value); 你可能需要在Table和ToDictionary()之间添加AsEnumerable()调用。 我不知道db.Table的确切类型。
此外,请更正第一个示例,你的第二个循环变量在声明和使用时不匹配。

2
因为 ToDictionary 是内存中的。不创建匿名对象意味着从数据库中检索所有列,而不仅仅是你需要的那些列。 - user247702
"ToDictionary" 并不知道底层结构,它只调用两个回调函数来获取键和值(在这种情况下是简单的属性选择 lambda 表达式),因此,这个 ToDictionary 调用应该在功能上等同于他提供的示例中的 foreach 循环。据我所知,所有的 LINQ 方法都是延迟执行的,也就是说只有在必要时才会调用回调函数。 - TWiStErRob
3
如果您使用 SQL Server Profiler 进行观察,您会发现在使用匿名对象时只选择了两列,而不使用时则会选择所有列。 - user247702

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