Sim*_*one 4 .net c# entity-framework async-await
任务模式说,为了保持一致,一切都必须完全异步或完全不同步.
首先使用实体框架设计器,我可以很容易地实现这一点
var course = await db.Courses.FindAsync(CourseID);
Run Code Online (Sandbox Code Playgroud)
课程是由实体框架生成的DbSet,因此具有所有Async方法.问题是,如果我向该类添加一个导航属性,后者不是DbSet,它不包含任何异步方法定义.
例如,如果我将一个导航属性添加到Students表,它将被创建为虚拟ICollection学生,这意味着我无法使用异步方法.但我真正想要的是拥有实体框架来自动生成Task <>,以便能够等待导航属性.
可能吗?我的目标是实现这样的目标:
var course = await db.Courses.FindAsync(CourseID);
var student = await course.Students.FindAsync(StudentID);
Run Code Online (Sandbox Code Playgroud)
而目前我的选择是混合async/notAsync代码:
var course = await db.Courses.FindAsync(CourseID);
var student = course.Students.First(p => p.ID = StudentID);
Run Code Online (Sandbox Code Playgroud)
或根本不使用导航属性:
var course = await db.Courses.FindAsync(CourseID);
var student = await db.Students.Where(p => p.CourseID == course.ID && p.ID == StudentsID).FirstAsync();
Run Code Online (Sandbox Code Playgroud)
你能建议一个不需要代码的解决方案吗?
编辑 根据https://entityframework.codeplex.com/wikipage?title=Task-based%20Asynchronous%20Pattern%20support%20in%20EF.#AsyncLazyLoading我搜索的内容称为"异步延迟加载"并且不是可用的功能然而(也许它永远不会).您似乎可以使用延迟加载或异步功能,也许我应该通过等待Task.Run(course.Students.First(p => p.ID = StudentID))将属性包装在Task中但是我是不确定这是个好主意.
在我看来,延迟加载(在我看来,枚举导航属性将触发数据库访问的情况)是一种糟糕的访问模式,因为它只是意味着数据库访问将发生在令人惊讶的地方,这可以使应用程序性能难以预测.
以下所有解决方案均使用导入System.Data.Entity
.
解决方案1:使用eager加载Include
var course = await db.Courses.Include(c => c.Students).FirstOrDefaultAsync(c => c.ID == CourseID);
var student = course.Students.First(p => p.ID == StudentID);
Run Code Online (Sandbox Code Playgroud)
好处:
Course
;Students
导航属性被加载并且可以自由使用;缺点:
Student
即使您只需要一个相关对象,也会加载整个相关对象;解决方案2:使用LoadAsync
具体集合类中存在的方法;
这个解决方案依赖于延迟加载的集合来自EntityCollection<TEntity>
类的事实.
首先,我将定义一个扩展方法:
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);
}
Run Code Online (Sandbox Code Playgroud)
然后你可以这样写:
var course = await db.Courses.FindAsync(CourseID);
await course.Students.LoadAsync();
var student = course.Students.First(p => p.ID = StudentID);
Run Code Online (Sandbox Code Playgroud)
优点:
Students
保证加载导航属性;缺点:
Course
和一系列相关的Student
对象可以变得陈旧,这可能会引发并发问题的道路; (请注意,影响关系的并发问题比影响单个记录的并发问题更难解决)解决方案3:使用CreateSourceQuery
具体类上的方法仅加载所需的Student
对象.
好吧,这样做不起作用,实际上是一个非常糟糕的主意.
但是,可以编写具有相同优点/缺点的解决方案,但另一方面:
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);
Run Code Online (Sandbox Code Playgroud)
优点:
Student
要使用的一个对象;缺点:
Students
导航属性没有被加载,这意味着它不能没有潜在触发数据库访问中使用;解决方案4:急切加载,更具选择性:在初始LINQ查询中加载您感兴趣的课程和学生.
我不是100%确定该解决方案将按照书面形式运行.
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;
Run Code Online (Sandbox Code Playgroud)
好处:
缺点:
Students
导航属性没有被加载,这意味着它不能没有潜在触发数据库访问中使用;**何时使用哪种解决方案?**
Course
已经加载了对象,则仅使用解决方案3 ; 归档时间: |
|
查看次数: |
1966 次 |
最近记录: |