c# - Build expression tree for LINQ using List<T>.Contains method -
problem
i'm working on refactoring linq
queries several reports in our web application, , i'm attempting move duplicate query predicates own iqueryable
exension methods can reuse them these reports, , reports in future. can infer, i've refactored predicate groups, predicate codes giving me problems. example of 1 of report methods have far:
dal method:
public list<entities.queryview> getqueryview(filter filter) { using (var context = createobjectcontext()) { return (from o in context.queryviews (!filter.fromdate.hasvalue || o.repairdate >= entityfunctions.truncatetime(filter.fromdate)) && (!filter.todate.hasvalue || o.repairdate <= entityfunctions.truncatetime(filter.todate)) select o) .withcode(filter) .ingroup(filter) .tolist(); } }
iqueryable
extension:
public static iqueryable<t> withcode<t>(this iqueryable<t> query, filter filter) { list<string> codes = dal.getcodesbycategory(filter.codecategories); if (codes.count > 0) return query.where(predicates.filterbycode<t>(codes)); return query; }
predicate:
public static expression<func<t, list<string>, bool>> filterbycode<t>(list<string> codes) { // method info list<string>.contains(code). var methodinfo = typeof(list<string>).getmethod("contains", new type[] { typeof(string) }); // list of codes call .contains() against. var instance = expression.variable(typeof(list<string>), "codes"); var param = expression.parameter(typeof(t), "j"); var left = expression.property(param, "code"); var expr = expression.call(instance, methodinfo, expression.property(param, "code")); // j => codes.contains(j.code) return expression.lambda<func<t, list<string>, bool>>(expr, new parameterexpression[] { param, instance }); }
the problem i'm having queryable.where
doesn't accept type of expression<func<t, list<string>, bool>
. way can think of creating predicate dynamically use 2 parameters, part stumping me.
what i'm not comprehending following method works. can pass exact lambda expression trying create dynamically, , correctly filters data.
public list<entities.queryview> getqueryview(filter filter) { // codes here. list<string> codes = dal.getcodesbycategory(filter.codecategories); using (var context = createobjectcontext()) { return (from o in context.queryviews (!filter.fromdate.hasvalue || o.repairdate >= entityfunctions.truncatetime(filter.fromdate)) && (!filter.todate.hasvalue || o.repairdate <= entityfunctions.truncatetime(filter.todate)) select o) .where(p => codes.contains(p.code)) // works fine. //.withcode(filter) .ingroup(filter) .tolist(); } }
questions
- can implement own
queryable.where
overload? if so, feasible? - if overload isn't feasible, there way dynamically construct predicate
p => codes.contains(p.code)
without using 2 parameters? - is there easier way this? feel i'm missing something.
you can create own extension method, name
where
, acceptiqueryable<t>
, returniqueryable<t>
, , otherwise make emulate form of linq methods. wouldn't be linq method, one. discourage writing such method because confuse others; if want make new extension method, use name not used in linq avoid confusion. in short, you're doing now, create new extensions without naming themwhere
. if wanted name 1where
though nothing's stopping you.sure, use lambda:
public static expression<func<t, bool>> filterbycode<t>(list<string> codes) t : icoded //some interface `code` field { return p => codes.contains(p.code); }
if cannot have entities implement interface (hint: can), code identical code have, using list pass in constant rather new parameter:
public static expression<func<t, bool>> filterbycode<t>(list<string> codes) { var methodinfo = typeof(list<string>).getmethod("contains", new type[] { typeof(string) }); var list = expression.constant(codes); var param = expression.parameter(typeof(t), "j"); var value = expression.property(param, "code"); var body = expression.call(list, methodinfo, value); // j => codes.contains(j.code) return expression.lambda<func<t, bool>>(body, param); }
i encourage use of former method; method loses static type safety, , more complex , such harder maintain.
another note, comment have in code:
// j => codes.contains(j.code)
isn't accurate. lambda actually looks is:(j, codes) => codes.contains(j.code);
noticeably different.see first half of #2.
Comments
Post a Comment