gpt4 book ai didi

c# - Entity Framework Designer 首先获取导航属性作为任务

转载 作者:太空狗 更新时间:2023-10-30 00:51:40 26 4
gpt4 key购买 nike

任务模式表明,为了保持一致,一切都必须完全异步或完全不异步。

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

var course = await db.Courses.FindAsync(CourseID);

Courses 是由 Entity Framework 生成的 DbSet,因此具有所有 Async 方法。问题是,如果我向该类添加导航属性,则后者不是 DbSet,并且它不包含任何异步方法定义。

例如,如果我将导航属性添加到 Students 表,它将被创建为虚拟 ICollection Students这意味着我不能使用异步方法。但我真正想要的是让 Entity Framework 自动生成一个 Task<>,以便能够等待导航属性。

这可能吗?我的目标是实现这样的目标:

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();

您能否建议一个不需要代码优先的解决方案?

编辑根据 https://entityframework.codeplex.com/wikipage?title=Task-based%20Asynchronous%20Pattern%20support%20in%20EF.#AsyncLazyLoading我搜索的是所谓的“异步延迟加载”,目前还没有可用的功能(也许永远不会)。看来您可以使用延迟加载或异步功能,也许我应该通过等待 Task.Run(course.Students.First(p => p.ID = StudentID)) 将属性包装在任务中,但我不确定这是个好主意。

最佳答案

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

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

解决方案 1:使用预先加载 Include

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

优点:

  • 加载所有对象所需的一次数据库访问 - 如果您想检索多个对象,此解决方案的扩展性非常好 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);
}

然后你可以这样写:

var course = await db.Courses.FindAsync(CourseID);
await course.Students.LoadAsync();
var student = course.Students.First(p => p.ID = StudentID);

优点:

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

缺点:

  • 易受“N+1查询”问题影响;
  • 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);

优点:

  • 你只加载一个Student您将要使用的对象;

缺点:

  • Students导航属性未加载,这意味着它不能在不触发数据库访问的情况下使用;
  • 第二行总会触发一次数据库访问(容易出现“N+1查询”问题,甚至运行方法次数);

解决方案 4:预先加载,更具选择性:在初始 LINQ 查询中加载您感兴趣的类(class)和学生。

我不能 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。仅当您明确拥有 Course 时才使用解决方案 3。对象已加载;

关于c# - Entity Framework Designer 首先获取导航属性作为任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25766025/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com