I'm trying to implement Repository and Unit Of Work in my Dotnet core project.
I have a line code like this:
services.AddScoped(typeof(IEfRepository<>), typeof(EfRepository<>));
public interface IEfRepository<TEntity> where TEntity : class
{
/// <summary>
/// SingleOrDefault expression
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
Task<TEntity> SingleOrDefaultAsync(Expression<Func<TEntity, bool>> predicate);
...
And implement:
public class EfRepository<TEntity> : IEfRepository<TEntity> where TEntity : class
{
/// <summary>
/// Fields
/// </summary>
private readonly DbContext _dbContext;
private DbSet<TEntity> _entities;
/// <summary>
/// Initialize a new instance of the <see cref=" EfRepository{TEntity}"/> class
/// </summary>
/// <param name="dbContext"></param>
public EfRepository(DbContext dbContext)
{
_dbContext = dbContext;
}
When request is sent from mediator. It is handled in Handler class and service is injected from contructor:
public class KiemtracongtrinhQueryHandler : IRequestHandler<GetKiemtracongtrinhQuery, GKiemtracongtrinhDto>,
IRequestHandler<ListKiemtracongtrinhQuery, IEnumerable<GKiemtracongtrinhDto>>
{
private readonly IGKiemtracongtrinhRepository _gKiemtracongtrinhRepository;
private readonly IMapper _mapper;
/// <summary>
/// Initialize a new instance of the <see cref="KiemtracongtrinhQueryHandler"/> class
/// </summary>
/// <param name="gKiemtracongtrinhRepository"></param>
/// <param name="mapper"></param>
public KiemtracongtrinhQueryHandler(IGKiemtracongtrinhRepository gKiemtracongtrinhRepository,
IMapper mapper)
{
_gKiemtracongtrinhRepository = gKiemtracongtrinhRepository ?? throw new ArgumentNullException(nameof(gKiemtracongtrinhRepository));
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
}
I think you are on the right way. Some fixes would be:
public class EfRepository<TEntity> : IEfRepository<TEntity> where TEntity : class
{
/// <summary>
/// Fields
/// </summary>
private readonly DbContext _dbContext;
/// <summary>
/// Initialize a new instance of the <see cref=" EfRepository{TEntity}"/> class
/// </summary>
/// <param name="dbContext"></param>
public EfRepository(DbContext dbContext)
{
_dbContext = dbContext;
}
/// <summary>
/// Initialize a new instance of the <see cref=" EfRepository{TEntity}"/> class
/// </summary>
/// <param name="dbContext"></param>
public EfRepository(DbContext dbContext)
{
_dbContext = dbContext;
}
///Example method
public async Task AddItem(T item)
{
_context.Set<T>().Add(item);
return await _context.SaveChangesAsync();
}
///Example method
public async Task<List<T>> GetAllRecords(T exampleItem)
{
return _context.Set<T>().ToList();
}
I can not see the reason DbEtities should be here.
2) Your handler:
public class KiemtracongtrinhQueryHandler : IRequestHandler<GetKiemtracongtrinhQuery, GKiemtracongtrinhDto>,
IRequestHandler<ListKiemtracongtrinhQuery, IEnumerable<GKiemtracongtrinhDto>>
{
private readonly IEfRepository<GKiemtracongtrinhRepository> _gKiemtracongtrinhRepository;
private readonly IMapper _mapper;
/// <summary>
/// Initialize a new instance of the <see cref="KiemtracongtrinhQueryHandler"/> class
/// </summary>
/// <param name="gKiemtracongtrinhRepository"></param>
/// <param name="mapper"></param>
public KiemtracongtrinhQueryHandler(IEfRepository<GKiemtracongtrinhRepository> gKiemtracongtrinhRepository,
IMapper mapper)
{
_gKiemtracongtrinhRepository = gKiemtracongtrinhRepository ?? throw new ArgumentNullException(nameof(gKiemtracongtrinhRepository));
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
}
In the handler you need to pass the generic repository.
If you want you can take a look at this example: https://github.com/steliosgiakoumidis/NotificationDatabase
Regarding the UnitOfWork, the way you can use it is the following:
public interface IUnitOfWork
{
IRepository2 Repository1{ get; }
IRepository2 Repository2 { get; }
IRepository3 Repository3 { get; }
Task<bool> SaveChangesAsync();
}
public class UnitOfWork : IUnitOfWork
{
public IRepository1 Repository1 { get; }
public IRepository2 Repository2 { get; }
public IRepository3 Repository3 { get; }
private readonly _dbContext;
public UnitOfWork(IRepository1 repository1, IRepository2 repository2,
IRepository3 repository3)
{
Repository1 = repository1;
Repository2 = repository2;
Repository3 = repository3;
}
public async Task<bool> SaveChangesAsync()
{
try
{
await _context.SaveChangesAsync();
}
catch (Exception ex)
{
Log.Error($"An error occured when saving changes to the database.)
}
}
}
It is a good practice to pass the SaveChanges in the UnitOfWork and execute the method after all database actions are complete. SaveChanges method is usually called not in the database/infrastructure layer of the application layer instead. Using the unit of work you can save quite a lot of injections. Since you bundle all your repositories into one UoW and you inject this instead.
Myself I have not used generic repostiries and UoW, but I prefered to use an abstract class instead, but the main idea is the same.