【问题标题】:use ToListAsync() with navigation property使用带有导航属性的 ToListAsync()
【发布时间】:2016-07-03 18:15:33
【问题描述】:

我正在使用带有实体框架的 ASP.NET MVC。 我有一个名为“AllUserData”的实体。 我有第二个实体,叫做“流派”。表格中的每一行都是一个流派。

这两个实体具有一对多的关系。所以在“AllUserData”类的类定义中,我有 公共虚拟 ICollection PreferredGenres { 获取;放; }

我能够成功阅读每个用户喜欢的流派

AllUserData aud = db.AllUserData.Single(b => b.UserId == currentUserId);
var chosengenres = aud.PreferredGenres.ToList()

但是我不能使用

AllUserData aud = db.AllUserData.Single(b => b.UserId == currentUserId);
var chosengenres = await aud.PreferredGenres.ToListAsync()

Visual Studio 说“ICollection 不包含 'ToListAsync' 的定义,并且最佳扩展方法重载 'QueryableExtensions.ToListAsync(IQueryable)' 需要 'IQueryable' 类型的接收器。

为什么会这样?两者之间的唯一区别是,在一种情况下我使用了 ToList(),而在另一种情况下我使用了 ToListAsync()。

异步方法不能与导航属性一起使用吗?在现实生活中的应用程序中,存在许多不同实体之间存在关系的情况;使用这些关系访问属性时可以不使用异步方法吗?有没有办法解决这个问题?如果可能,我宁愿异步做事。

【问题讨论】:

    标签: asp.net-mvc entity-framework


    【解决方案1】:

    您的导航属性将被声明为 ICollection,这是实体属性的标准 - 编译器不会高兴。

    但是,您可以在导航集合和 ToListAsync() 之间使用 AsQueryable 或另一个 Select()(如果这对您的需要有意义)获得 IQueryable。

    ToList() 可以对 IEnumerable(包括 ICollection 和 IQueryable)进行操作。 ToListAsync() 仅适用于 IQueryable,因此来自 VS 的消息。 “为什么”可能在于实现细节。

    【讨论】:

    • 特别是,使用 IQueryable 意味着 ToListAsync 不必等待查询在服务器上执行并返回——这有点意思:你要求它在后台运行您的查询,完成后返回到代码中的当前位置。
    • 在发帖之前,我实际上尝试过“await aud.PreferredGenres.AsQueryable().ToListAsync()”。虽然 VS 没有抱怨,但当我真正尝试运行它时,它在运行时引发了异常。无论出于何种原因,这似乎也不起作用。您说另一种方法可能是使用 Select()。你的意思是我应该在 ToListAsync() 之前调用它?
    • 仅供参考,我使用 AsQueryable().ToListAsync() 时收到的错误是 - “源 IQueryable 未实现 IDbAsyncEnumerable。只能使用实现 IDbAsyncEnumerable 的源用于实体框架异步操作。有关更多详细信息,请参阅go.microsoft.com/fwlink/?LinkId=287068。"
    • 是的,问题可能是查询已经被解决了;也许试试:AllUserData aud = await db.AllUserData.Single(b => b.UserId == currentUserId).Select(u => u.PreferredGenres).ToListAsync(); 让整个查询发生在服务器上。
    • 不幸的是,这也不起作用。我收到这条消息 - “AllUserData 不包含‘Select’的定义,并且找不到接受‘AllUserData’类型的第一个参数的扩展方法‘Select’”
    【解决方案2】:

    好的,看来实体框架不允许以这种方式异步调用导航属性。

    对我有用的是在开始时更愿意加载导航属性。 (感谢用户 daf 向我发送了正确的方向)。

    所以不是

    AllUserData aud = await db.AllUserData.SingleAsync(b => b.UserId == currentUserId);
    var usergenres = await aud.PreferredGenres.ToListAsync(); 
    

    这不起作用,我可以这样做:

    AllUserData aud = await db.AllUserData.Include(p=>p.PreferredGenres).SingleAsync(b => b.UserId == currentUserId);
    var usergenres = aud.PreferredGenres.ToList();
    

    这样,第一个查询要求通过使用 .Include() 预先加载从数据库中提取所有必要的信息,并且由于 .SingleAsync() 的用户而异步执行此操作。下一条语句使用 .ToList(),但不应再次访问数据库,因为数据已经预先加载,因此它是异步的并不重要。整个操作现在是异步的。

    我也意识到之前在 StackOverflow 上也有人问过同样的问题;根据您使用的搜索词,找到它有点困难:Entity Framework Designer First get navigation property as Tasks。所选答案提供了 4 种不同的解决方案,其中第一个与我现在使用的类似。

    在定义实体类时,似乎也可以将导航属性指定为异步 - 请参阅此页面上标题为“异步延迟加载”的部分:here 示例 sn-p 演示了如何为单个导航属性执行此操作;我不知道它是否也可以用于 ICollection 导航属性,这是我需要的,但我也没有做任何进一步的挖掘。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-02
      • 1970-01-01
      • 1970-01-01
      • 2013-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-08
      相关资源
      最近更新 更多