The package NBB.Data.EntityFramework provides some implementations formalized in NBB.Data.Abstractions using Entity Framework:
EfCrudRepository
EfReadOnlyRepository
EfUow
- unit of work implementationEfQuery
- implementation ofIQueryable<TEntity>
andIAsyncEnumerable<TEntity>
dotnet add package NBB.Data.EntityFramework
The EfCrudRepository is a generic CRUD repository based on EF Core, that implements the ICrudRepository interface from package NBB.Data.Abstractions
.
Although the use of generic repository is controversial, they say that you should only use single purpose repositories as dictated by the business domain, you may find this useful when implementing data access for CRUD domains.
You have to explicitly register the generic repository for a specific entity with the container:
services.AddEfCrudRepository<Payable, PaymentsDbContext>();
Example usages:
public async Task Handle(EventStoreEnvelope<ContractLineAdded> @event, CancellationToken cancellationToken)
{
var de = @event.Event;
var e = await _contractReadModelRepository.GetByIdAsync(de.ContractId, cancellationToken, "ContractLines");
if (e != null)
{
if (e.ContractLines.All(cl => cl.ContractLineId != de.ContractLineId))
{
var contractLine = new ContractLineReadModel(de.ContractLineId, de.Product, de.Price, de.Quantity, de.ContractId);
e.ContractLines.Add(contractLine);
await _contractReadModelRepository.SaveChangesAsync(cancellationToken);
}
}
}
For read models, if you need a clean separation, you can provide read-only repositories, although you may need a CRUD one for read model generators.
services.AddEfReadOnlyRepository<Invoice, InvoicesDbContext>();
When writing read-model queries, you may not want to add another layer of abstraction, such as repositories, but still, you may not want to work directly with EF objects in your Application Layer (Query handlers). If this is your case, you can choose to work with IQueryable and this is exactly what this package facilitates.
services.AddEfQuery<Invoice, InvoicesDbContext>();
After this registration, you may inject IQueryable<Invoice>
in your query handler or directly in the controller:
[Route("api/[controller]")]
public class InvoicesController : Controller
{
private readonly IQueryable<Invoice> _invoiceQuery;
public InvoicesController(IQueryable<Invoice> invoiceQuery)
{
_invoiceQuery = invoiceQuery;
}
// GET api/invoices
[HttpGet]
public Task<List<Invoice>> Get()
{
return _invoiceQuery.ToListAsync();
}
This package offers EfUow<TEntity, TContext>
as an implementation for the IUow<TEntity>
from the abstractions package. This default implementation only delegates the SaveChangesAsync
to the underlying DbContext.
When working with unit of work repositories you need to register the unit of work EfUow
, like so:
AddEfUow<TEntity, TContext>(this IServiceCollection services)
Registering an EfCrudRepository
with AddEfCrudRepository
will auto register a unit of work.
You need to call AddEntityFrameworkDataAccess
somewhere in the composition root, that registers some internal services, like so:
AddEntityFrameworkDataAccess(this IServiceCollection services)