简体   繁体   中英

How to create an asynchronous repository with EF Core?

I'm trying to abstract EF Core with a repository and can't figure out how to make it asynchronous. This is the synchronous version:

public class UserRepository
{
    private DbSet<User> users;
    public UserRepository(ApplicationDbContext context)
    {
        users = context.Set<User>();
    }
    public IQueryable<User> GetAll()
    {
        return users;
    }
    ...
}

The problem is that in order to make the query asynchronous, we need to use the special extension methods from EF like ToListAsync() or CountAsync() , and if the project that uses the repository references EF namespaces, what's the point of the abstraction?

Another thing I looked into was returning IAsyncEnumerable<User> , and then using this official library to use LINQ asynchronously. However, I couldn't get it to behave properly because when using these new LINQ methods, the query is processed in .NET rather than converting the LINQ syntax to SQL. Let's take the following query as an example:

var result = await Repository.GetAll()
    .Limit(10)
    .ToListAsync();

When using EF's extension methods, I saw this query in SSMS:

exec sp_executesql N'SELECT [u].[Id], [u].[Deleted], [u].[EmailAddress], [u].[FirstName], [u].[LastName], [u].[PhoneNumber], [u].[Status], [u].[VerifiedEmailAddress], [u].[VerifiedPhoneNumber]
FROM [Users] AS [u]
ORDER BY (SELECT 1)
OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY',N'@__p_0 int,@__p_1 int',@__p_0=0,@__p_1=10

Meaning only 10 records will be selected from the DB, which is what I wanted.

But when using System.Linq.Async 's extension methods, I still got the same result back to my C# code, but this query showed in SSMS:

SELECT [u].[Id], [u].[Deleted], [u].[EmailAddress], [u].[FirstName], [u].[LastName], [u].[PhoneNumber], [u].[Status], [u].[VerifiedEmailAddress], [u].[VerifiedPhoneNumber]
FROM [Users] AS [u]

No limit at all, meaning if I had millions of rows in the Users table, all of them would be selected every time I run the query, when I only need the first 10.

Is there a way to work with LINQ to SQL and async streams? Or maybe another idea of how to abstract EF but still allow async querying?

I do not recommend abstracting EFCore, you can abstract the context using an IApplicationDbContext . Mostly because EF is already an abstract layer and secondly because leaving 'users' access to the IQueryable could create unexpected behavior or exceptions.

If you really need to do this, what I am thinking about is something like:

public Task<List<User>> GetAsync(Action<IQueryable<User>> filter)
{
    filter(_users);
    return _users.ToListAsync();
}

and using it like:

var result = await Repository.GetAsync(x => x.Take(10));

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