Skip to content

Latest commit

 

History

History

NBB.Data.EntityFramework

NBB.Data.EntityFramework

The package NBB.Data.EntityFramework provides some implementations formalized in NBB.Data.Abstractions using Entity Framework:

  • EfCrudRepository
  • EfReadOnlyRepository
  • EfUow - unit of work implementation
  • EfQuery - implementation of IQueryable<TEntity> and IAsyncEnumerable<TEntity>

NuGet install

dotnet add package NBB.Data.EntityFramework

CRUD Repositories

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);
        }
    }
}

Read-Only Repositories

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>();

Using Queries for read model

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();
        }

Unit of work

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.

Internal services registration

You need to call AddEntityFrameworkDataAccess somewhere in the composition root, that registers some internal services, like so:

AddEntityFrameworkDataAccess(this IServiceCollection services)