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'

Respuestas:


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

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

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

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.


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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.