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.whereoverload? 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 1wherethough 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