如何在LINQ中使用async和await?

6
我是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?


尝试使用 .Select(...) https://msdn.microsoft.com/zh-cn/library/bb548891(v=vs.100).aspx - Zhi Kai
3个回答

6

在 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/zh-cn/library/dn220257(v=vs.113).aspx

注意使用“new Region”来解决匿名类型序列化的问题(我无法对Scott的答案进行评论)。

更新

因为您试图部分加载模型(仅加载 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();

不要忘记更新您的响应类型!


2
如果您正在进行服务层开发,您可能会对使用OData v4的WEB API 2感兴趣。您可以保护和返回部分模型,而无需将其作为JSON使用DTO / VM。这取决于您是要创建网站还是数据服务。 - Sproulx
2
这里有一个教程,讲解了Sproulx所说的内容,我也推荐这种方法。 - Scott Chamberlain
1
如果你想向前看,学习新的Web技术,我建议你阅读有关AngularJS的内容,以便轻松地查询使用WebAPI 2和OData构建的数据服务。这三种技术非常搭配。 - Sproulx
嘿@Sproulx - 感谢您的评论。我一定会的!请告诉我您知道的任何结合上述三种语言的好学习资源/教程。 - 90abyss
1
Scott Chamberlain的教程是学习OData和Web Api的好起点。然后,我阅读了这篇教程,以了解如何混合mvc和AngularJS(并学习AngularJS的基础知识)。在那之后,您将能够将OData / Web Api与AngularJS混合使用。对于一个真正的应用程序(不仅仅是为了学习),我强烈建议将前端应用程序和数据服务拆分为两个不同的Visual Studio解决方案。这样更容易扩展。 - Sproulx
显示剩余4条评论

1
你可以用 WhereToListAsync() 的组合替换 FirstOrDefaultAsync
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<someAnnonamousType>

现在出现了一个异常:类型 '<>f__AnonymousType3`2[System.Int32,System.String]' 无法序列化。请考虑使用 DataContractAttribute 属性标记它,并使用 DataMemberAttribute 属性标记您想要序列化的所有成员。如果该类型是集合,请考虑使用 CollectionDataContractAttribute 进行标记。有关其他支持的类型,请参阅 Microsoft .NET Framework 文档。 - 90abyss
我对ASP.NET不是很熟悉,也不知道“正确”的修复方法,但如果我在编写它,我会停止使用匿名类,并在new中使用一个真正的类,该类具有适当的注释以使其可序列化。 - Scott Chamberlain
当我执行以下代码时:"var region = await ...Select(r => new Region() { ... })...."然后我会得到这个异常:无法在LINQ to Entities查询中构造实体或复杂类型“x.Models.Region”。 - 90abyss
1
尝试将where放在select之前。另外,Region是否与db.Regions中使用的Region相同?当我说“使用真实类”时,我的意思是创建一个新的DTO类进行分配,而不是重用现有的实体。请参见[Sproulx的答案](https://dev59.com/4Jjga4cB1Zd3GeqPPMno#38287593)以获取我所说内容的完整示例。 - Scott Chamberlain

0
在这种情况下,如果您想要所有记录,应该删除对 FirstOrDefaultAsync() 的调用,这将返回类似以下的 IEnumerable<anonymoustype>。您可以使用 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));

你不能再等待这段代码了,因为你现在没有任何返回可等待对象的东西。 - Scott Chamberlain
@Rahul 正如 Scott 指出的那样,我遇到了这个错误:“无法等待 System.Linq.IQueryable”。 - 90abyss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接