简体   繁体   English

异步 Linq 查询

[英]Asynchronous Linq Query

I am learning Linq.我正在学习 Linq。 The example uses the Northwind database.该示例使用 Northwind 数据库。 I am trying to make the following method asynchronous:我正在尝试使以下方法异步:

public IActionResult ProductsThatCostMoreThan(decimal price)
{
    var model = db.Products
        .Include(p => p.Category)
        .Include(p => p.Supplier)
        .AsEnumerable()
        .Where(p => p.UnitPrice > price);
    return View(model);
}

If have tried the code below but the compiler complains IAsyncEnumerable<Product> does not contain a definition for Where and no accessible extension method 'Where' accepting a first argument of type IAsyncEnumerable<Product> could be found.如果尝试了下面的代码,但编译器抱怨IAsyncEnumerable<Product>不包含Where的定义,并且找不到接受IAsyncEnumerable<Product>类型的第一个参数的可访问扩展方法“Where”。 Removing the where expression resolves this issue.删除 where 表达式可以解决此问题。 How can I write an asynchronous where filter?如何编写异步 where 过滤器?

public async Task<IActionResult> ProductsThatCostMoreThanAsync(decimal price)
{
    var model = await db.Products
       .Include(p => p.Category)
       .Include(p => p.Supplier)
       .AsAsyncEnumerable()
       .Where(p => p.UnitPrice > price);
    return View(model);
}

Attempt at clarification: I will try to explain the context of the question better.尝试澄清:我将尝试更好地解释问题的背景。 This is an academic exercise from a book from which I am trying to learn Linq among other things.这是一本书的学术练习,我试图从中学习 Linq 等。

The method is an controller action method in a Asp.net Core MVC website.该方法是 Asp.net Core MVC 网站中的控制器操作方法。 I have learned that if a controller action method is slow (for example it needs to get a lot of data from a database) it limits scalability of the website because the method locks a thread from the web server thread pool until it completes.我了解到,如果控制器操作方法很慢(例如它需要从数据库中获取大量数据),它会限制网站的可扩展性,因为该方法会锁定 Web 服务器线程池中的线程,直到它完成。 Making action methods in the controller asynchronous releases the thread back into the pool so it can be reused while the method does something slow.使控制器中的操作方法异步将线程释放回池中,以便在方法执行缓慢的操作时可以重用它。 (see https://docs.microsoft.com/en-us/archive/msdn-magazine/2014/october/async-programming-introduction-to-async-await-on-asp-net ). (请参阅https://docs.microsoft.com/en-us/archive/msdn-magazine/2014/october/async-programming-introduction-to-async-await-on-asp-net )。

For example:例如:

public IActionResult Index()
{
    var model = new HomeIndexViewModel
    {
        Categories = db.Categories.ToList()
    };
    return View(model);
}

Is changed to the following for that purpose:为此目的更改为以下内容:

public async Task<IActionResult> Index()
{
    var model = new HomeIndexViewModel
    {
         Categories = await db.Categories.ToListAsync(),
    };
    return View(model);
}

I am trying to make a similar change to the method at the top of my question.我正在尝试对问题顶部的方法进行类似的更改。

The Async comes in when you actually do something.当你真正做某事时, Async就会出现。

As long as you're setting up the query, from a task perspective it doesn't make sense to make thing asynchronous.只要您正在设置查询,从task角度来看,使事情异步就没有意义。

I'll give you an example;我给你举个例子;

Setting up the query设置查询

The following code is about setting up the query:以下代码是关于设置查询的:

var query = db.Products
    .Include(p => p.Category)
    .Include(p => p.Supplier)
    .Where(p => p.UnitPrice > price)
    .OrderBy(c => c.SomeProperty)
    .Etc...;

This query above is not executed, until, you actually enumerate the result.上面的查询不会执行,直到您实际枚举结果。 Hence;因此; upon this point an async Task is not relevant (well, in 99.99999% of the cases).在这一点上, async Task不相关(好吧,在 99.99999% 的情况下)。


Executing Async执行异步

So when do you use the Task ( async if you like)那么你什么时候使用Task (如果你喜欢async

You use it when materializing the query;在具体化查询时使用它; ie; IE; execute/enumerate it:执行/枚举它:

The most famous example is this:最著名的例子是这样的:

var result = await query.ToListAsync();

This by the way is why you have many more helpers like this:顺便说一下,这就是为什么你有更多这样的助手:

  • SingleAsync,单异步,
  • FirstAsync,首先异步,

The question:问题:

How can I write an asynchronous where filter?如何编写异步 where 过滤器?

You most likely don't need to.你很可能不需要。 It's the execution of the filter that you would like to be asynchronous, ie;您希望异步执行过滤器,即; the access to your database or other source.访问您的数据库或其他来源。

It's when materializing, ie;它是在实现时,即; ToListAsync -ing, which you want to execute as a Task . ToListAsync -ing,您希望将其作为Task执行。

I am assuming you use EntityFamework here.我假设您在这里使用 EntityFamework。 In this case (as other answers point out) you can opt for .ToListAsync() like so:在这种情况下(正如其他答案指出的那样),您可以像这样选择.ToListAsync()

public async IActionResult ProductsThatCostMoreThan(decimal price)
{ 
    var model = await db.Products
                               .Include(p => p.Category)
                               .Include(p => p.Supplier)
                               .Where(p => p.UnitPrice > price).ToListAsync();
     return View(model); 
}

The reason you have to have it at the end of your chain is because it returns Task , not IEnumerable, so you have to await for it.您必须将它放在链的末尾的原因是它返回Task ,而不是 IEnumerable ,因此您必须等待它。 Technically you can enumerate your data source without filter, await for it and then apply .Where.从技术上讲,您可以在没有过滤器的情况下枚举您的数据源,等待它,然后应用 .Where。 But the way EF works it complies your .Where clause into SQL code and so it must be part of original method chain但是 EF 的工作方式会将您的.Where子句纳入 SQL 代码,因此它必须是原始方法链的一部分

Another point to make here it this extension method is part of EF assembly and therefore you might have had to look for alternatives had you not used EF.在这里要说明的另一点是,此扩展方法是 EF 程序集的一部分,因此如果您不使用 EF,您可能不得不寻找替代方法。 As other answers point out, actual query execution happens when you make a call to enumerate the IEnumerable, so you could technically write your own extension method with similar effect (easiest will be to examine the EF's extension source)正如其他答案指出的那样,当您调用枚举 IEnumerable 时会发生实际查询执行,因此您可以在技术上编写自己的具有类似效果的扩展方法(最简单的方法是检查 EF 的扩展源)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM