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
Post a Comment