Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 实体框架设计器首先将导航属性作为任务获取_C#_.net_Entity Framework_Async Await - Fatal编程技术网

C# 实体框架设计器首先将导航属性作为任务获取

C# 实体框架设计器首先将导航属性作为任务获取,c#,.net,entity-framework,async-await,C#,.net,Entity Framework,Async Await,任务模式表示,为了保持一致,所有内容都必须完全异步或完全不异步 通过首先使用实体框架设计器,我可以很容易地实现这一点 var course = await db.Courses.FindAsync(CourseID); Courses是实体框架生成的数据库集,因此具有所有异步方法。 问题是,如果向该类添加导航属性,则后者不是DbSet,并且不包含任何异步方法定义 例如,如果我向Students表添加导航属性,它将作为虚拟ICollection Students创建 这意味着我不能使用异步方法。

任务模式表示,为了保持一致,所有内容都必须完全异步或完全不异步

通过首先使用实体框架设计器,我可以很容易地实现这一点

var course = await db.Courses.FindAsync(CourseID);
Courses是实体框架生成的数据库集,因此具有所有异步方法。 问题是,如果向该类添加导航属性,则后者不是DbSet,并且不包含任何异步方法定义

例如,如果我向Students表添加导航属性,它将作为虚拟ICollection Students创建 这意味着我不能使用异步方法。 但我真正想要的是让实体框架自动生成任务,以便能够等待导航属性

可能吗?我的目标是实现以下目标:

var course = await db.Courses.FindAsync(CourseID);
var student = await course.Students.FindAsync(StudentID);
目前,我的选项混合了异步/非同步代码:

var course = await db.Courses.FindAsync(CourseID);
var student = course.Students.First(p => p.ID = StudentID);
或者根本不使用导航属性:

var course = await db.Courses.FindAsync(CourseID);
var student = await db.Students.Where(p => p.CourseID == course.ID && p.ID == StudentsID).FirstAsync();
您能建议一个不需要先编写代码的解决方案吗

编辑 根据im搜索的东西被称为“异步延迟加载”,目前还不是一个可用的特性(也许永远也不会)。
似乎您可以使用延迟加载或异步功能,也许我应该通过等待Task.Run(course.Students.First(p=>p.ID=StudentID))将属性包装到任务中,但我不确定这是一个好主意。

在我看来,延迟加载(在我看来,枚举导航属性会触发数据库访问)是一种糟糕的访问模式,因为这仅仅意味着数据库访问将发生在令人惊讶的地方,这可能会使应用程序性能难以预测

以下所有解决方案都使用从
System.Data.Entity
导入

解决方案1:使用包含
的即时加载

var course = await db.Courses.Include(c => c.Students).FirstOrDefaultAsync(c => c.ID == CourseID);
var student = course.Students.First(p => p.ID == StudentID);
优点:

  • 加载所有对象所需的一个数据库访问—如果您希望一次检索多个
    课程
    对象,此解决方案可以很好地扩展
  • 学生
    导航属性已加载,可以自由使用
缺点:

  • 始终至少有一个数据库访问
  • 即使您只需要一个,也会加载一整套相关的
    Student
    对象
解决方案2:使用混凝土集合类上存在的
LoadAsync
方法

此解决方案依赖于这样一个事实:延迟加载的集合来自类

首先,我将定义一个扩展方法:

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);
}
优点:

  • 如果对象已加载到上下文中,则可能根本没有数据库访问权限
  • 保证加载导航属性
    学生
缺点:

  • 易受“N+1查询”问题影响
  • 课程
    和相关的
    学生
    对象集都可能过时,这可能会引发并发问题;(注意,影响关系的并发问题比影响单个记录的并发问题更难解决)
解决方案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);
优点:

  • 您只加载一个要使用的
    Student
    对象
缺点:

  • Students
    导航属性未加载,这意味着在不触发数据库访问的情况下无法使用该属性
  • 第二行总是会触发数据库访问(易受“N+1查询”问题的影响,甚至会多次运行该方法)
解决方案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;
优点:

  • 检索两个对象只需要一次数据库访问
  • 您只检索要处理的对象
缺点:

  • Students
    导航属性未加载,这意味着在不触发数据库访问的情况下无法使用该属性
**什么时候使用哪种解决方案**

  • 如果您需要填充导航属性(因为您知道您将使用其大多数元素,或者因为您希望将父实体传递给另一个允许以任何方式使用该属性的组件),则使用解决方案1或2
  • 如果您不需要填写导航属性,则使用解决方案4。如果您已明确加载
    课程
    对象,则仅使用解决方案3

学生的声明类型是什么?
学生.查找方法从何而来?Sry Jean,没有查找方法。我编辑了这个问题,谢谢。学生课程是EFL生成的另一个课程唯一但完美的解决方案可能是第三个,但我有很多问题:唯一但完美的解决方案对我来说,这可能是第三个选项,但它不起作用:转换为System.Data.Entity.Core.Objects.DataClasses.EntityCollection always return null=>always return collection.AsQueryable()=>等待结果导致此异常:EntityFramework.dll中发生了类型为“System.InvalidOperationException”的异常,但未在用户代码中处理其他信息:源IQueryable的提供程序未实现IDBSyncQueryProvider。只有实现IDBSyncQueryProvider的提供程序才能用于实体Fra梅沃
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;