【问题标题】:How to use async and await in LINQ?如何在 LINQ 中使用异步和等待?
【发布时间】:2016-07-10 00:36:18
【问题描述】:

我是 ASP.NET 的新手,正在尝试处理一个示例项目。我知道这是一个愚蠢的问题,但请多多包涵。即使在 SQL 查询中返回了多行,我下面的代码也只返回一个结果。我意识到 FirstOrDefaultAsync 只返回第一个元素。我在这里浏览了文档:https://msdn.microsoft.com/en-us/library/system.data.entity.queryableextensions(v=vs.113).aspx,但我找不到使用什么来代替 FirstOrDefaultAsync,它不仅会返回第一行,还会返回与我的 SQL 条件匹配的所有内容。

这是我目前的代码:

    [ResponseType(typeof(Region))]
    public async Task<IHttpActionResult> GetRegion(int id)
    {

        var region_id = from sr in db.ServiceToRegions
                           where sr.Service_ID == id
                           select sr.Region_ID;

        var region = await db.Regions.Select(r =>
            new 
            {
                Region_ID = r.Region_ID,
                Region_Name = r.Region_Name
            }).FirstOrDefaultAsync(r => region_id.Contains(r.Region_ID)); //QQQ

        if (region == null)
        {
            return NotFound();
        }

        return Ok(region);
    }

我应该使用什么来代替 FirstOrDefaultAsync?

【问题讨论】:

标签: c# linq asp.net-web-api


【解决方案1】:

你应该在 Where: 之后使用 ToListAsync()

var region = await db.Regions
    .Select(r =>
        new Region
        {
            Region_ID = r.Region_ID,
            Region_Name = r.Region_Name
        }
    )
    .Where(r => region_id.Contains(r.Region_ID))
    .ToListAsync();

https://msdn.microsoft.com/en-us/library/dn220257(v=vs.113).aspx

注意“新区域”以解决您的匿名类型问题以进行序列化(我无法评论 Scott 的回答)。

UDPATE

因为您尝试部分加载模型(只有 2 个属性),所以您需要使用这样的 ViewModel:

public class RegionVM
{
    public int Region_ID { get; set; }
    public string Region_Name { get; set; }
}

然后为此更改您的代码:

var region = await db.Regions
    .Select(r =>
        new RegionVM
        {
            Region_ID = r.Region_ID,
            Region_Name = r.Region_Name
        }
    )
    .Where(r => region_id.Contains(r.Region_ID))
    .ToListAsync();

别忘了更新您的回复类型!

【讨论】:

  • 如果我这样做会得到另一个异常:无法在 LINQ to Entities 查询中构造实体或复杂类型“x.Models.Region”
  • 如果您正在做服务层,您可能会对带有 OData v4 的 WEB API 2 感兴趣。您可以在不使用 DTO/VM 作为 json 的情况下保护和返回部分模型。这取决于您是在制作网站还是数据服务。
  • Here is a tutorial 关于 Sproulx 所说的,我也推荐这种方法。
  • 如果您想展望和学习新的 Web 技术,我建议您阅读有关 AngularJS 以查询(轻松!)使用 webapi 2 和 odata 构建的数据服务。这 3 种技术可以很好地协同工作。
  • Scott Chamberlain 的教程是 OData 和 Web Api 的一个很好的起点。然后,我阅读了this tutorial 来学习如何混合 mvc 和 AngularJS(并学习 AngularJS 的基础知识)。之后,您将能够将 OData/Web Api 与 AngularJS 混合使用。对于真正的应用程序(不仅仅是为了学习),我强烈建议将前端应用程序和数据服务拆分为两个不同的 Visual Studio 解决方案。以这种方式更容易扩展。
【解决方案2】:

您可以将FirstOrDefaultAsync 替换为WhereToListAsync() 的组合

var region = await db.Regions.Select(r =>
        new 
        {
            Region_ID = r.Region_ID,
            Region_Name = r.Region_Name
        }).Where(r => region_id.Contains(r.Region_ID)).ToListAsync();

这将返回一个List&lt;someAnnonamousType&gt;

【讨论】:

  • 现在遇到异常:无法序列化类型“f__AnonymousType3`2[System.Int32,System.String]”。考虑使用 DataContractAttribute 属性对其进行标记,并使用 DataMemberAttribute 属性标记您想要序列化的所有成员。如果该类型是一个集合,请考虑使用 CollectionDataContractAttribute 对其进行标记。有关其他支持的类型,请参阅 Microsoft .NET Framework 文档。
  • 我不是最熟悉 ASP.NET,我不知道修复它的“正确”方法,但如果我正在编写它,我将不再使用匿名类并使用new 中的一个真实类,它具有适当的注释以使其可序列化应用。
  • 当我执行以下操作时:“var region = await ...Select(r => new Region() { ... })....” 然后我得到这个异常:实体或无法在 LINQ to Entities 查询中构造复杂类型“x.Models.Region”。
  • 尝试将 where 放在 select 之前。另外,Region 是否与 db.Regions 使用的 Region 相同?当我说“使用真正的类”时,我要创建一个新的 DTO 类来分配,而不是重用现有的实体。请参阅 Sproulx's answer 了解我所说的完整示例。
【解决方案3】:

如果您想要所有记录,您应该删除对FirstOrDefaultAsync() 的调用,这将返回IEnumerable&lt;anonymoustype&gt;,如下所示。您可以使用Where() LINQ 扩展方法包含条件

    var region = await db.Regions                      
                  .Select(r =>
                      new 
                       {
                         Region_ID = r.Region_ID,
                         Region_Name = r.Region_Name
                       }).Where(r => region_id.Contains(r.Region_ID));

【讨论】:

  • 你不能再等待代码了,你现在没有任何东西可以返回一个等待对象。
  • @Rahul 正如斯科特指出的那样,我收到了这个错误:“无法等待 System.Linq.IQueryable”
猜你喜欢
  • 1970-01-01
  • 2020-07-11
  • 2014-03-19
  • 2016-05-02
  • 2013-04-27
  • 2015-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多