Reputation: 24212
I'm starting with this:
query
.Take(20)
.Select(item => new
{
id = item.SomeField,
value = item.AnotherField
})
.AsEnumerable()
.ToDictionary(item => item.id, item => item.value);
Now, I want to reuse everything except SomeField
and AnotherField
.
public static Dictionary<int, string> ReusableMethod<T>(
this IQueryable<T> query,
Func<T, int> key,
Func<T, string> value)
{
return query
.Take(20)
.Select(item => new
{
id = key(item),
value = value(item)
})
.AsEnumerable()
.ToDictionary(item => item.id, item => item.value);
}
query.ReusableMethod(item => item.SomeField, item => item.AnotherField);
This works, but the DB query selects more data than required, so I guess that means ReusableMethod
is using linq-to-objects.
Is it possible to do this while only selecting the required data? I'll add that Func<> is still part magic for me, so I might be missing something obvious.
Clarification to avoid confusion: the Take(20)
is fine, the Select()
isn't.
Upvotes: 2
Views: 2401
Reputation: 1647
Recently I had the same problem and here is what I did:
You have some DbEntity (generated by LINQ to EF,SQL), but you want to query only some fields (I did this to save network bandwidth). You have to create class derived from DbEntity, beacuse you cant create anonyous types in Expression trees and you can not create new instance of DbEntity in select statement. (No need to add any fields, properties etc.)
public class LocalEntity : DbEntity {}
You need to define a method to generate your select expression tree. It should look like this. This will generate expression tree similar to this: .Select(db => new LocalEntity() { Property1 = db.Property1, Proeprty2 = db.Property2})
protected Expression<Func<DbEntity, LocalEntity>> getSelectExpression()
{
ParameterExpression paramExpr = Expression.Parameter(typeof(DbEntity), "dbRecord");
var selectLambda = Expression.Lambda<Func<DbEntity, LocalEntity>>(
Expression.MemberInit(
Expression.New(typeof(LocalEntity)),
Expression.Bind(typeof(LocalEntity).GetProperty("DbEntityFieldName"), Expression.Property(paramExpr, "DbEntityFieldName"),
....
))
),
paramExpr);
return selectLambda;
}
Use it like this:
query.Select(getSelectExpression()).ToDictionary();
Consider this more as pseudo-code than C# code, as I had to simplify it a lot and I can´t test it, but if oyu make it work, it will transfer from DB only fields you define in getSelectedExpression, not the whole row.
Upvotes: 1
Reputation: 190943
Wrap your funcs with Expression
and remove the AsEnumerable
call.
public static Dictionary<int, string> ReusableMethod<T>(
this IQueryable<T> query,
Expression<Func<T, int>> key,
Expression<Func<T, string>> value)
An alternative would be to just return the whole row then. No need for Expression
in this case.
return query
.Take(20)
.ToDictionary(key, value);
Upvotes: 3