Entity Framework设计首先获得导航属性的任务 [英] Entity Framework Designer First get navigation property as Tasks

查看:117
本文介绍了Entity Framework设计首先获得导航属性的任务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

该工作模式说,为了保持一致的一切必须完全异步或完全不异步。

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 achieve this quite easily

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

课程是实体框架生成的DbSet,因此具有所有的异步方法。 的问题是,如果我添加导航属性该类别,后者不是DbSet并且它不包含任何异步方法定义

Courses is a DbSet generated by entity framework and therefore has all the Async methods. The problem is that if I add a navigation property to that class, the latter is not a DbSet and it does not contain any async method definition.

举个例子,如果我增加一个导航属性​​到学生表中,将创建一个虚拟的ICollection学生 这意味着我不能使用异步方法。 但我确实希望是具有实体框架自动生成任务<>为了能够连等待的导航属性。

As an example, if I add a navigation property to a Students table, it will be created as a virtual ICollection Students which means I cannot use the async methods. But what I do want is having entity framework to automatically generate a Task<> in order to be able to await even the navigation properties.

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

Is It possible? my goal is to achieve something like this:

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

而在那一刻我的选择是混合异步/ notAsync code:

while at the moment my options are mixing async/notAsync code:

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

或不使用导航属性都:

or not using the navigation property at all:

var course = await db.Courses.FindAsync(CourseID);
var student = await db.Students.Where(p => p.CourseID == course.ID && p.ID == StudentsID).FirstAsync();

您能否提供不需要code第一个解决方案?

Can you suggest a solution that do not require code first?

修改 根据<一个href="https://entityframework.$c$cplex.com/wikipage?title=Task-based%20Asynchronous%20Pattern%20support%20in%20EF.#AsyncLazyLoading" rel="nofollow">https://entityframework.$c$cplex.com/wikipage?title=Task-based%20Asynchronous%20Pattern%20support%20in%20EF.#AsyncLazyLoading什么即时搜索被称为异步延迟加载,并且是不可用的功能还没有(也许它永远不会)。 看来,您可以使用延迟加载或异步的特点,也许我应该只是等待Task.Run包装在一个任务属性(course.Students.First(P => p.ID = StudentID)),但我不知道这是一个好主意。

EDIT according to https://entityframework.codeplex.com/wikipage?title=Task-based%20Asynchronous%20Pattern%20support%20in%20EF.#AsyncLazyLoading what im searching for is called "Async Lazy Loading" and is not a feature available yet (and maybe it will never be). It seems that you can either use Lazy Loading OR the async features, maybe I should just wrap the property in a Task by awaiting Task.Run(course.Students.First(p => p.ID = StudentID)) but I'm not sure it is a good idea.

推荐答案

在我看来,延迟加载(这在我看来是这样的,其中列举的导航属性会触发数据库访问)是一个坏的访问模式,如它只是意味着将数据库访问发生在令人惊讶的地方,它可以使应用程序的性能难以predict。

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.

以下所有使用的解决方案,从进口 System.Data.Entity的

All the solutions below use an import from 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);

优势:

  • 所需装载的所有对象的一个​​数据库访问 - 这解决方案扩展,如果你想恢复的时间超过一个课程目标非常好;
  • 学生导航属性被加载并可以自由使用;
  • One database access that is required to load all objects - and this solution scales very well if you want to retrieve more than one Course object at a time;
  • The Students navigation property is loaded and can be used freely;

缺点:

  • 总是有至少一个数据库的访问;
  • 在整组相关学生,即使你只需要一个对象被加载;
  • There is always at least one database access;
  • The whole set of related Student objects is loaded even if you needed only one;

解决方案2 :使用 LoadAsync 方法存在于具体的集合类;

Solution 2: Use the LoadAsync method that exists on the concrete collection class;

该解决方案依赖于一个事实,即延迟加载集合是从 EntityCollection&LT; TEntity&GT;

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

优势:

  • 有可能是在所有如果在上下文中已经加载的对象没有数据库的访问;
  • 的导航属性学生是保证被加载;
  • There may be no database access at all if the objects are already loaded in the context;
  • The navigation property Students is guaranteed to be loaded;

缺点:

  • 容易受到N + 1查询的问题;
  • 无论是课程和组相关的学生对象可以变得枯燥乏味,这可能会引发并发问题下来路; (请注意,影响的关系更难解决比影响单个记录并发问题的并发问题)
  • Susceptible to the "N+1 queries" issue;
  • Both the 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)

解决方案3 使用的具体类的 CreateSourceQuery 方法只加载学生您想要的对象。

Solution 3: Use the CreateSourceQuery method on the concrete class to load only the Student object that you want.

确定,这样做是不行的,而实际上是pretty的坏主意。

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

优势:

  • 您只加载一个学生的对象,你要使用;
  • You only load the one Student object that you are going to use;

缺点:

  • 学生导航属性不加载,这意味着它不能没有可能引发数据库访问使用;
  • 在第二行总会触发数据库访问(容易受到N + 1查询的问题,甚至在运行方法次);
  • The Students navigation property is not loaded, meaning that it cannot be used without potentially triggering a database access;
  • The second line will always trigger a database access (susceptible to the "N+1 queries" issue, or even running the method times);

解决方案4 :预先加载,更有选择性:加载两个课程和学生,在最初的LINQ查询您的兴趣

Solution 4: Eager loading, more selective: load both the course and the student that interest you in the initial LINQ query.

我不是100%肯定该解决方案将作为写的。

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;

优势:

  • 只有一个数据库访问需要获取两个对象;
  • 您只检索你要工作的对象;

缺点:

  • 学生导航属性不加载,这意味着它不能没有可能引发数据库访问使用;
  • The Students navigation property is not loaded, meaning that it cannot be used without potentially triggering a database access;

**何时使用哪种解决方案? **

** When to use which solution? **

  • 如果您需要导航属性填充(或者因为你知道你会利用大部分的元素,或者因为要父实体传递给被允许使用该属性然而,另一个组件就是了),然后使用解决方案1或2;
  • 如果您不需要导航属性被填充,然后使用解决方案4.只使用解决方案3,如果你有明确的课程对象已经装载;
  • If you need the navigation property to be filled (either because you know that you will make use of most of its elements, or because you want to pass the parent entity to another component that is allowed to make use of that property however it wants), then use solution 1 or 2;
  • If you do not need the navigation property to be filled, then use solution 4. Only use solution 3 if you categorically have the Course object already loaded;

这篇关于Entity Framework设计首先获得导航属性的任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆