The Task pattern says that in order to be consistent everything has to be completely async or completely not async.
By using entity framework designer first I can ac
In my opinion, lazy loading (which would in my opinion be the case where enumerating the navigation property would trigger a database access) is a bad access pattern, as it simply means that database access will happen at surprising places, which can make application performance difficult to predict.
All the solutions below use an import from System.Data.Entity
.
Solution 1: Use eager loading with an 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:
Course
object at a time;Students
navigation property is loaded and can be used freely;Drawbacks:
Student
objects is loaded even if you needed only one;Solution 2: Use the LoadAsync
method that exists on the concrete collection class;
This solution relies on the fact that lazy-loaded collections are from the EntityCollection<TEntity> class.
First, I would define an extension method:
public static async Task LoadAsync<T>(ICollection<T> collection)
where T : class
{
if (collection == null) throw new ArgumentNullException("collection");
var entityCollection = collection as System.Data.Entity.Core.Objects.DataClasses.EntityCollection<T>;
if (entityCollection == null || entityCollection.IsLoaded) return;
await entityCollection.LoadAsync(CancellationToken.None).ConfigureAwait(false);
}
Then you could write something like:
var course = await db.Courses.FindAsync(CourseID);
await course.Students.LoadAsync();
var student = course.Students.First(p => p.ID = StudentID);
Advantage:
Students
is guaranteed to be loaded;Drawbacks:
Course
and the set of related Student
objects can grow stale, which may trigger concurrency issues down the road; (note that concurrency issues that affect a relationship are harder to resolve than concurrency issues that affect a single record)Solution 3: Use the CreateSourceQuery
method on the concrete class to load only the Student
object that you want.
OK, doing that does not work, and actually is a pretty bad idea.
However, a solution with the same advantages/drawbacks can be written, but in another way:
var course = await db.Courses.FindAsync(CourseID);
var studentsQuery = from c in db.Courses
where c.ID == CourseID
from s in c.Students
select s;
var student = await studentsQuery.FirstAsync(p => p.ID = StudentID);
Advantage:
Student
object that you are going to use;Drawbacks:
Students
navigation property is not loaded, meaning that it cannot be used without potentially triggering a database access;Solution 4: Eager loading, more selective: load both the course and the student that interest you in the initial LINQ query.
I am not 100% sure that that solution will work as written.
var query = from c in db.Courses
where 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:
Drawbacks:
Students
navigation property is not loaded, meaning that it cannot be used without potentially triggering a database access;** When to use which solution? **
Course
object already loaded;