Here's a smarter ToDictionary method that's built off of IQueryable and not IEnumerable. It integrates with EntityFramework to ensure that you select only the minimal number of columns needed, instead of fetching them all and projecting out only two columns after everything's in memory.
Usage:
var colorDictionary = this.Db.Items.ToDictionary(x => x.Id, x => x.Color);
// results in the following SQL:
// SELECT
// [Extent1].[Id] AS [Id],
// [Extent1].[Color] AS [Color]
// FROM [dbo].[Item] AS [Extent1]
Code:
public static class IQueryableExtensions
{
public static IDictionary<KeyType, ValueType> ToDictionary<T, KeyType, ValueType>(this IQueryable<T> input, Expression<Func<T, KeyType>> keySelector, Expression<Func<T, ValueType>> valueSelector)
{
// deconstruct keySelector and valueSelector as member expressions
var keyAccess = (keySelector.Body as MemberExpression)?.Member; // discard the "x => x" part of x => x.Name, retain .Name
var valueAccess = (valueSelector.Body as MemberExpression)?.Member;
if(keyAccess == null || valueAccess == null)
{
throw new ArgumentException("keySelector and valueSelector must be of the form 'x => x.MyProperty'");
}
var keyType = (keySelector.Body as MemberExpression).Type;
var valueType = (valueSelector.Body as MemberExpression).Type;
// Make a new expression, x => new Pair<keyType,valueType> { Key = x.Key, Value = x.Value }
var pairType = typeof(Pair<,>).MakeGenericType(keyType, valueType);
var xParam = Expression.Parameter(typeof(T));
var newExpression =
Expression.Lambda(
Expression.MemberInit(
Expression.New(pairType),
Expression.Bind(pairType.GetProperty("Key"), Expression.MakeMemberAccess(xParam, keyAccess)),
Expression.Bind(pairType.GetProperty("Value"), Expression.MakeMemberAccess(xParam, valueAccess))),
xParam)
as Expression<Func<T, Pair<KeyType, ValueType>>>;
var pParam = Expression.Parameter(pairType);
return input.Select(newExpression).AsEnumerable().ToDictionary(kv => kv.Key, kv => kv.Value);
}
private class Pair<K, V>
{
public K Key { get; set; }
public V Value { get; set; }
}
}
No comments:
Post a Comment