c# - Entity Framework Designer First get navigation property as Tasks -
the task pattern says in order consistent has async or not async.
by using entity framework designer first can achieve quite
var course = await db.courses.findasync(courseid); courses dbset generated entity framework , therefore has async methods. problem if add navigation property class, latter not dbset , not contain async method definition.
as example, if add navigation property students table, created virtual icollection students means cannot use async methods. want having entity framework automatically generate task<> in order able await navigation properties.
is possible? goal achieve this:
var course = await db.courses.findasync(courseid); var student = await course.students.findasync(studentid); while @ moment options mixing async/notasync code:
var course = await db.courses.findasync(courseid); var student = course.students.first(p => p.id = studentid); or not using navigation property @ all:
var course = await db.courses.findasync(courseid); var student = await db.students.where(p => p.courseid == course.id && p.id == studentsid).firstasync(); can suggest solution not require code first?
edit according https://entityframework.codeplex.com/wikipage?title=task-based%20asynchronous%20pattern%20support%20in%20ef.#asynclazyloading im searching called "async lazy loading" , not feature available yet (and maybe never be). seems can either use lazy loading or async features, maybe should wrap property in task awaiting task.run(course.students.first(p => p.id = studentid)) i'm not sure idea.
in opinion, lazy loading (which in opinion case enumerating navigation property trigger database access) bad access pattern, means database access happen @ surprising places, can make application performance difficult predict.
all solutions below use import system.data.entity.
solution 1: use eager loading include
var course = await db.courses.include(c => c.students).firstordefaultasync(c => c.id == courseid); var student = course.students.first(p => p.id == studentid); advantages:
- one database access required load objects - , solution scales if want retrieve more 1
courseobject @ time; - the
studentsnavigation property loaded , can used freely;
drawbacks:
- there @ least 1 database access;
- the whole set of related
studentobjects loaded if needed one;
solution 2: use loadasync method exists on concrete collection class;
this solution relies on fact lazy-loaded collections entitycollection<tentity> class.
first, define extension method:
public static async task loadasync<t>(icollection<t> collection) t : class { if (collection == null) throw new argumentnullexception("collection"); var entitycollection = collection system.data.entity.core.objects.dataclasses.entitycollection<t>; if (entitycollection == null || entitycollection.isloaded) return; await entitycollection.loadasync(cancellationtoken.none).configureawait(false); } then write like:
var course = await db.courses.findasync(courseid); await course.students.loadasync(); var student = course.students.first(p => p.id = studentid); advantage:
- there may no database access @ if objects loaded in context;
- the navigation property
studentsguaranteed loaded;
drawbacks:
- susceptible "n+1 queries" issue;
- both
course, set of relatedstudentobjects can grow stale, may trigger concurrency issues down road; (note concurrency issues affect relationship harder resolve concurrency issues affect single record)
solution 3: use createsourcequery method on concrete class load student object want.
ok, doing not work, , pretty bad idea.
however, solution same advantages/drawbacks can written, in way:
var course = await db.courses.findasync(courseid); var studentsquery = c in db.courses c.id == courseid s in c.students select s; var student = await studentsquery.firstasync(p => p.id = studentid); advantage:
- you load 1
studentobject going use;
drawbacks:
- the
studentsnavigation property not loaded, meaning cannot used without potentially triggering database access; - the second line trigger database access (susceptible "n+1 queries" issue, or running method times);
solution 4: eager loading, more selective: load both course , student interest in initial linq query.
i not 100% sure that solution work written.
var query = c in db.courses c.id == courseid select new { course = c, student = c.students.first(p => p.id == studentid) }; var result = await query.firstordefaultasync(); var course = result.course; var student = result.student; advantages:
- only 1 database access required retrieve both objects;
- you retrieve objects going work on;
drawbacks:
- the
studentsnavigation property not loaded, meaning cannot used without potentially triggering database access;
** when use solution? **
- if need navigation property filled (either because know make use of of elements, or because want pass parent entity component allowed make use of property wants), use solution 1 or 2;
- if not need navigation property filled, use solution 4. use solution 3 if categorically have
courseobject loaded;
Comments
Post a Comment