The library contains couple of useful classes and extensions to register DbContext in ServiceCollection.
The library is dependent to core version of entity framework and external library UnitOfWork
The library provides helper methods for both API projects and test (unit and integration) projects.
Install the packages from NuGet repository
Install-Package Easify.Ef
Install-Package Easify.Ef.Testing
or
dotnet add package Easify.Ef
dotnet add package Easify.Ef.Testing
To use the library in any projects you can register context using the following snippet:
services.AddSqlDbContext<DbContextClass>(Configuration.GetConnectionString("DbContextConnectionString"));
The library provides some base classes and extensions which facilitate dealing with different scenarios in the development. The original library provides the full implementation of unit of work which can be useful to every projects. Here is a sample of the usage:
public CompanyListRequestHandler(IUnitOfWork<DealManagementDbContext> unitOfWork,
ILogger<CompanyListRequestHandler> logger)
{
_unitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task<IEnumerable<CompanyDto>> Handle(CompanyListRequest request,
CancellationToken cancellationToken)
{
if (request == null) throw new ArgumentNullException(nameof(request));
_logger.LogInformation($"Handling company list request");
var repository = await _unitOfWork.GetRepository<Company>();
// Return the list of the companies
}
Built-in support is available for CRUD operation as one or many operations.
The library provides extended list of the extensions
IEnumerable<TEntity> GetList<TEntity>(Specification<TEntity> spec,
Func<IQueryable<TEntity>, IQueryable<TEntity>> extend = null)
Task<IEnumerable<TEntity>> GetListAsync<TEntity>(Specification<TEntity> spec,
Func<IQueryable<TEntity>, IQueryable<TEntity>> extend = null)
TEntity GetFirstItemOrDefault<TEntity>(Specification<TEntity> spec,
Func<IQueryable<TEntity>, IQueryable<TEntity>> extend = null)
Task<TEntity> GetFirstItemOrDefaultAsync<TEntity>(Specification<TEntity> spec,
Func<IQueryable<TEntity>, IQueryable<TEntity>> extend = null)
Here is some samples from the usage:
Simple Query
var companies = await _unitOfWork.GetRepository<Company>().GetListAsync(q => q.OrderBy(c => c.CompanyName));
Query with includes and paging
var deals = await _unitOfWork.GetRepository<Deal>().GetListAsync(new ActiveDealsSpecification(),
q => q.OrderBy(m => m.DealName).Include(m => m.Company).Include(m => m.Team));
Using this class as a base class for DbContext will help you to set auditing information automatically.
In such scenarios that you would like to persist LastModifiedDate and LastModifierBy, You classes need to implement either of these two interfaces:
- IAuditable interface
- Auditable abstract class
In both cases DbContextBase has a support for this and in each SaveChanges (or Async version) call it updates these fields automatically.
To use the library for test projects:
services.AddInMemoryDbContext<DbContextClass>();
OR
services.AddInMemoryDbContext<DbContextClass>("in memory context name");
Also there are couple of extensions which helps to facilitate the unit testing. Here is the list:
TDbContext ToDbContext<T, TDbContext>(this IEnumerable<T> entities, Action<TDbContext> action = null)
IUnitOfWork<TDbContext> ToTypedUnitOfWork<T, TDbContext>(this IEnumerable<T> entities, Action<TDbContext> action = null)
And this is a sample usage:
[Fact]
public async Task Should_DealRequestListHandler_ReturnAllDealsInAListWhenCalled()
{
// Given
var unitOfWork = _fixture.Deals.ToTypedUnitOfWork<Deal, DealManagementDbContext>();
var logger = Substitute.For<ILogger<DealListRequestHandler>>();
var sut = new DealListRequestHandler(unitOfWork, _fixture.TypeMapper, logger);
// When
var result = (await sut.Handle(new DealListRequest(), CancellationToken.None)).ToList();
// Then
result.Should().NotBeNull().And.HaveCount(3);
}
Also there are some helper classes and methods which are facilitating the testing:
- TypeMapper
- FakeOperationContext