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
course
object @ time; - the
students
navigation property loaded , can used freely;
drawbacks:
- there @ least 1 database access;
- the whole set of related
student
objects 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
students
guaranteed loaded;
drawbacks:
- susceptible "n+1 queries" issue;
- both
course
, set of relatedstudent
objects 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
student
object going use;
drawbacks:
- the
students
navigation 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
students
navigation 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
course
object loaded;
Comments
Post a Comment