Linq-to-SQL ToDictionary ()

81

¿Cómo convierto correctamente dos columnas de SQL (2008) usando Linq en un diccionario (para almacenamiento en caché)?

Actualmente recorro el IQueryable b / c No puedo hacer que funcione el método ToDictionary. ¿Algunas ideas? Esto funciona:

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

Lo que realmente me gustaría hacer es algo como esto, que no parece funcionar:

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

Pero obtengo este error: No se puede convertir de 'System.Linq.IQueryable' a 'System.Collections.Generic.IEnumerable'

Codewerks
fuente

Respuestas:

121
var dictionary = db
    .Table
    .Select(p => new { p.Key, p.Value })
    .AsEnumerable()
    .ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
;
yfeldblum
fuente
Lo siento, es posible que no haya sido claro, lo intenté antes, pero esto crea un Dictionary <string, #Anonymous Type>, no Dictionary <string, string>
Codewerks
Prueba kvp => kvp.Value as string. El punto de la respuesta fue .AsEnumerable().
yfeldblum
Gracias, sí, pensé que se necesitaba AsEnumerable, pero la llamada ToDictionary todavía no funciona. Ambas columnas son varchars en SQL, por lo que regresan como cadenas, pero no puedo averiguar cómo hacer que se
rellenen
No sé si esto sigue siendo relevante, pero ¿por qué es AsEnumerablenecesario? Para mí también funciona sin eso, pero probablemente Linq ya haya cambiado (han pasado 7 años)
Alexander Derck
1
@AlexanderDerck - En ese momento, AsEnumerableera necesario. No recuerdo exactamente por qué, después de todo este tiempo, pero una posible explicación es que ToDictionaryera un método de extensión activado IEnumerable, pero la interfaz para Linq-to-SQL no IEnumerable.
yfeldblum
16

Solo está definiendo la clave, pero también debe incluir el valor:

var dic = (from p in db.Table
             select new {p.Key, p.Value })
            .ToDictionary(p => p.Key, p=> p.Value);
Christian C. Salvadó
fuente
3
-1 El error es que la <string, string>pieza hace que utilice la public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer);sobrecarga en lugar de la public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector);sobrecarga.
user247702
Creo que todavía necesita .AsEnumerable()antes.ToDictionary()
AceMark
funcionó perfectamente ... finalmente el que estaba buscando
Brian
9

Gracias chicos, sus respuestas me ayudaron a solucionar este problema, deberían ser:

var dic = db
        .Table
        .Select(p => new { p.Key, p.Value })
        .AsEnumerable()
        .ToDictionary(k=> k.Key, v => v.Value);
Codewerks
fuente
5
La razón por la que necesita AsEnumerable () es porque LINQ to SQL no mezcla el procesamiento local y remoto (SQL), por lo que esto hace que la primera parte se ejecute en el servidor SQL y luego la parte posterior final se ejecute localmente usando LINQ to Objects que puede do Dictionarys :)
DamienG
Tiene sentido. Además, el segundo parámetro en ToDictionary necesita sus propios argumentos Func, lo que me estaba molestando antes.
Codewerks
1

¿Por qué crearía un objeto anónimo para cada elemento de la tabla solo para convertirlo?

Simplemente podría usar algo como: IDictionary<string, string> dic = db.Table.ToDictionary(row => row.Key, row => row.Value); Es posible que deba incluir una llamada AsEnumerable () entre Table y ToDictionary (). No sé el tipo exacto de db.Table.


También corrija la primera muestra, su segunda variable de ciclo no coincide en la declaración y el uso.

TWiStErRob
fuente
2
Porque ToDictionaryestá en memoria. No crear un objeto anónimo significa que todas las columnas se recuperan de la base de datos en lugar de solo las que necesita.
user247702
ToDictionaryno conoce la estructura subyacente, solo llama a las dos devoluciones de llamada para la clave y el valor (en este caso, la propiedad simple seleccionando lambdas), por lo que esta ToDictionaryllamada debería ser funcionalmente equivalente al foreach en el ejemplo que proporcionó. Hasta donde yo sé, todos los métodos LINQ son diferidos, lo que significa que las devoluciones de llamada solo se llaman cuando es necesario.
TWiStErRob
3
Si observa con SQL Server Profiler, verá que con un objeto anónimo solo se seleccionan dos columnas y sin él se seleccionan todas las columnas.
user247702