c# - How to create a reusable where clause for EF6 -


i have moved coding in java c# , still learning various elements of c#.

to access existing database, cannot redesign, using entity frameworks 6 , 'code first database' generate contexts , types representing database tables. using ling-to-sql retrieve data database heavily denormalized.

my current task create report each section read various tables, have relationship 1 base table.

this working example:

using(var db = new paymentcontext())  {     var out = pay in db.payment               join typ in db.type on new { pay.id, pay.typeid } equals                                       new { typ.id, typ.typeid }               join base in db.basetable on                    new { pay.key1, pay.key2, pay.key3, pay.key4, pay.key5 } equals                    new { base.key1, base.key2, base.key3, base.key4, base.key5 }                              base.cancelled.equals("0") &&               base.timestamp.compareto(starttime) > 0 &&               base.timestamp.compareto(endtime) < 1 &&               .               (other conditions)               .               group new { pay, typ } new { typ.paymenttype } grp               select new               {                   name = grp.key,                   count = grp.count(),                   total = grp.sum(x => x.pay.amount)               }; } 

there large number of sections in report , each section generate clause contain conditions shown. in sections, required data extracted tables 5 levels below basetable.

what want create resuable clause each report section, avoid lot of duplicated code.

after lot of searching, tried use solution suggested here , has been superseded in entity framework 6.

how avoid duplicating code unnecessarily?

the nice thing linq methods where() return ienumerable<t> can feed next method.

you refactor where clauses extension methods akin to:

public static class paymentqueryextensions {      public static iqueryable<t> applynotcancelledfilter(         iqueryable<t> payments)          t : basetable {          // no explicit 'join' needed access properties of base class in ef model         return payments.where(p => p.cancelled.equals("0"));     }      public static iqueryable<t> applytimefilter(         iqueryable<t> payments, datetime starttime, datetime endtime)          t: basetable {          return payments.where(p => p.timestamp.compareto(starttime) > 0                                  && p.timestamp.compareto(endtime) < 1);     }      public static igrouping<typ, t> groupbytype(          iqueryable<t> payments)           t: basetable {          // assuming relationship payment -> typ has been set backlink property payment.typ         // e.g. ef fluent api:          //  modelbuilder.entity<typ>().hasmany(t => t.payment).withrequired(p => p.typ);         return payments.groupby(p => p.typ);     } } 

and compose queries using these building blocks:

ienumerable<payment> payments = db.payment     .applynotcancelledfilter()     .applytimefilter(starttime, endtime);  if (rendersectionone) {     payments = payments.applysectiononefilter(); }  var paymentsbytype = payments.groupbytype();  var result = paymentsbytype.select(new           {               name = grp.key,               count = grp.count(),               total = grp.sum(x => x.pay.amount)           } ); 

now have composed query, execute enumerating. no db access has happened until now.

var output = result.toarray(); // <- db access happens here 

edit after suggestion of ivan, looked @ our codebase. mentioned, extension methods should work on iqueryable instead of ienumerable. take care use expressions can translated sql, i.e. not call custom code overriden tostring() method.

edit 2 if payment , other model classes inherit basetable, filter methods can written generic methods accept child type of basetable. added example grouping method.


Comments