How do I do an async query without enumerating the data?

asynchronous c# entity-framework-core

Question

I'm trying to work out how to write a generic async GetAll<T> database method in Entity Framework Core without enumerating the data. I want GetAll<T> to be async, but don't want it to enumerate the data, so that I can add a Where method afterwards, and have that applied to the database query..

List<Customer> customers = await _customerRepository.GetAll()
  .Where(c => c.Name.StartsWith("a"))
  .ToList();

MSDN's article on async queries shows the following...

public async Task<List<Blog>> GetBlogsAsync()
{
    using (var context = new BloggingContext())
    {
        return await context.Blogs.ToListAsync();
    }
}

...but (if I understand it correctly) the call to ToListAsync() will enumerate the data, meaning it will load the entire Blogs data set into memory, and only afterwards apply any filtering that a Where method applied to the result would specify.

Can I have an async method that returns an IEnumerable<T> instead?

I'm quite happy not to use a repository if that's a better way to do it. This article suggests that EF Core doesn't benefit from repositories, but I can't see how to do what I want his way either.

Update: To clarify (thanks to DavidG), the idea is that my ASP.NET Core controllers can have an injected repository, and can do an async query to get the data. I was thinking of making the repository generic, and doing the filtering in the controller, hence the question. I know I can write a CustomersRepository and have a CustomersThatStartWith method, but then the repository gets bloated. I was looking for as simple a repository as posisble.

1
1
4/14/2019 9:25:51 PM

Accepted Answer

The safest thing to do is allow your caller (in your case the controller, though I would recommend keeping your logic outside of them) to pass in their own filters. Something like this for example:

public async Task<List<T>> GetAsync<T>(params Expression<Func<T, bool>>[] filters)
{
    IQueryable<T> query = _context.Set<T>();    

    if(filters != null)
    {
        foreach (var filter in filters)
        {
            query = query.Where(filter);
        }
    }

    return await query.ToListAsync();
}

And call it like this:

var allUsers = await repository.GetAsync<User>();

var zombies = await repository.GetAsync<User>(u => u.IsUndead == true);

var zombieDevelopers = await repository.GetAsync<User>(
    u => u.IsUndead = true,
    u => u.JobTitle = "Developer");
3
4/14/2019 9:35:39 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow