Creating an Async version of a Generic Repository

11,057

What you're doing is async over sync, and it is an anti-pattern when dealing with async work.

EF6 already comes out of the box with an async api, which doesn't use any new threads behind the scenes, but uses the async IO work exposed by the database to consume its work.

When you go async over sync, you usually end up spinning up a bunch of threads which basically sit there and wait for the query to return and that doesn't scale well at all, and uses redundant threads which aren't really needed when dealing with IO bound operations. more over, when people see an async method, they never say "he's probably using a new thread to run the synchronous version", they usually say "this async implementation will save me unnecessary thread use for IO bound work"

Here is a simple UpdateAsync method done with EF6:

public static async Task<string> UpdateAsync(Customer obj)
{
    NorthwindEntities db = new NorthwindEntities();

    // This is an async method
    Customer existing = await db.Customers.FindAsync(obj.CustomerID);         
    existing.CompanyName = obj.CompanyName;
    existing.ContactName = obj.ContactName;
    existing.Country = obj.Country;

    await db.SaveChangesAsync(); // This is an async method

    return "Customer updated successfully!";
}

You can read more about EF6 and async method:

  1. Performing Asynchronous Operations Using Entity Framework
  2. Async Query & Save (EF6 onwards)
Share:
11,057
MHOOS
Author by

MHOOS

Learning new things everyday.

Updated on June 04, 2022

Comments

  • MHOOS
    MHOOS over 1 year

    I have a Generic repository implementing the following interface :

    public interface IRepository
    {
      IUnitOfWork UnitOfWork { get; }
    
    
      IEnumerable<TEntity> GetWithRawSql<TEntity>(string query, params object[] parameters) where TEntity : class;
    
      TEntity GetByKey<TEntity>(object keyValue) where TEntity : class;
    
      IQueryable<TEntity> GetQuery<TEntity>() where TEntity : class;
    
      IQueryable<TEntity> GetQuery<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class;
    
      IQueryable<TEntity> GetQuery<TEntity>(ISpecification<TEntity> criteria) where TEntity : class;
    
      TEntity Single<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class;
    
      TEntity Single<TEntity>(ISpecification<TEntity> criteria) where TEntity : class;
    
      TEntity First<TEntity>(Expression<Func<TEntity, bool>> predicate) where TEntity : class;
    
      TEntity First<TEntity>(ISpecification<TEntity> criteria) where TEntity : class;
    
      void Add<TEntity>(TEntity entity) where TEntity : class;
    
      void Attach<TEntity>(TEntity entity) where TEntity : class;
    
      void Delete<TEntity>(TEntity entity) where TEntity : class;
    
      void Delete<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class;
    
      void Delete<TEntity>(ISpecification<TEntity> criteria) where TEntity : class;
    
      void Update<TEntity>(TEntity entity) where TEntity : class;
    
      IEnumerable<TEntity> Find<TEntity>(ISpecification<TEntity> criteria) where TEntity : class;
    
      IEnumerable<TEntity> Find<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class;
    
      TEntity FindOne<TEntity>(ISpecification<TEntity> criteria) where TEntity : class;
    
      TEntity FindOne<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class;
    
      IEnumerable<TEntity> GetAll<TEntity>() where TEntity : class;
    
      IEnumerable<TEntity> Get<TEntity, TOrderBy>(Expression<Func<TEntity, TOrderBy>> orderBy, int pageIndex,
        int pageSize, SortOrder sortOrder = SortOrder.Ascending) where TEntity : class;
    
      IEnumerable<TEntity> Get<TEntity, TOrderBy>(Expression<Func<TEntity, bool>> criteria,
        Expression<Func<TEntity, TOrderBy>> orderBy, int pageIndex, int pageSize,
        SortOrder sortOrder = SortOrder.Ascending) where TEntity : class;
    
      IEnumerable<TEntity> Get<TEntity, TOrderBy>(ISpecification<TEntity> specification,
        Expression<Func<TEntity, TOrderBy>> orderBy, int pageIndex, int pageSize,
        SortOrder sortOrder = SortOrder.Ascending) where TEntity : class;
    
      int Count<TEntity>() where TEntity : class;
    
      int Count<TEntity>(Expression<Func<TEntity, bool>> criteria) where TEntity : class;
    
      int Count<TEntity>(ISpecification<TEntity> criteria) where TEntity : class;
    }
    

    This interface belongs to the modified version of a generic repository as implemented by huyrua's blog. I need to have async version of some of the methods. For instance I have the following implementation for GetWithRawSql method:

    public IEnumerable<TEntity> GetWithRawSql<TEntity>(string query, params object[] parameters) where TEntity : class
    {
        return DbContext.Set<TEntity>().SqlQuery(query, parameters).ToList();
    }
    

    and for the Async version I have implemented the following:

    public async Task<IEnumerable<TEntity>> GetWithRawSqlAsync<TEntity>(string query, params object[] parameters) where TEntity : class
    {
        return await Task.Run(() => GetWithRawSql<TEntity>(query, parameters));
    }
    

    or the same for GetByKey method :

    public async Task<TEntity> GetByKeyAsync<TEntity>(object keyValue) where TEntity : class
    {
        return await Task.Run(() => GetByKey<TEntity>(keyValue));
    }
    

    Above implementations looks like a quick and dirty work around. How would you implement above methods and what problems I might encounter using

    await Task.Run(() => GetWithRawSql<TEntity>(query, parameters));
    

    in my repository? For full implantation you can have a look at the blog and for your information I use Entity framework 6.1.1.