简体   繁体   中英

Asynchronous Linq Query

I am learning Linq. The example uses the Northwind database. 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. Removing the where expression resolves this issue. How can I write an asynchronous where filter?

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.

The method is an controller action method in a Asp.net Core MVC website. 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. 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 ).

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.

As long as you're setting up the query, from a task perspective it doesn't make sense to make thing asynchronous.

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


Executing Async

So when do you use the Task ( async if you like)

You use it when materializing the query; 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?

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 .

I am assuming you use EntityFamework here. In this case (as other answers point out) you can opt for .ToListAsync() like so:

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. Technically you can enumerate your data source without filter, await for it and then apply .Where. But the way EF works it complies your .Where clause into SQL code and so it must be part of original method chain

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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