Skip to main content
The Intent.EntityFrameworkCore.Repositories module provides a complete repository pattern implementation for Entity Framework Core, including base repository classes, interfaces, and generated repository implementations for each domain entity.

Overview

This module generates:
  • Repository interfaces for each entity
  • Repository implementations using EF Core
  • Base repository class with common CRUD operations
  • Support for async and sync methods
  • Paged query support
  • Custom query methods
  • Stored procedure support

Installation

Intent.EntityFrameworkCore.Repositories

Key Components

Repository Base

The RepositoryBase<TDomain, TPersistence, TDbContext> class provides common repository operations:
RepositoryBase
public class RepositoryBase<TDomain, TPersistence, TDbContext> : IEFRepository<TDomain, TPersistence>
    where TDomain : class
    where TPersistence : class, TDomain
    where TDbContext : DbContext, IUnitOfWork
{
    protected readonly TDbContext _dbContext;

    public RepositoryBase(TDbContext dbContext)
    {
        _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
    }

    public IUnitOfWork UnitOfWork => _dbContext;

    // CRUD Operations
    public virtual void Add(TDomain entity)
    {
        GetSet().Add((TPersistence)entity);
    }

    public virtual void Update(TDomain entity)
    {
        GetSet().Update((TPersistence)entity);
    }

    public virtual void Remove(TDomain entity)
    {
        GetSet().Remove((TPersistence)entity);
    }

    // Query Operations
    public virtual async Task<TDomain?> FindAsync(
        Expression<Func<TPersistence, bool>> filterExpression,
        CancellationToken cancellationToken = default)
    {
        return await QueryInternal(filterExpression)
            .SingleOrDefaultAsync<TDomain>(cancellationToken);
    }

    public virtual async Task<List<TDomain>> FindAllAsync(
        CancellationToken cancellationToken = default)
    {
        return await QueryInternal(filterExpression: null)
            .ToListAsync<TDomain>(cancellationToken);
    }

    public virtual async Task<List<TDomain>> FindAllAsync(
        Expression<Func<TPersistence, bool>> filterExpression,
        CancellationToken cancellationToken = default)
    {
        return await QueryInternal(filterExpression)
            .ToListAsync<TDomain>(cancellationToken);
    }

    // Paged Queries
    public virtual async Task<IPagedList<TDomain>> FindAllAsync(
        int pageNo,
        int pageSize,
        CancellationToken cancellationToken = default)
    {
        var query = QueryInternal(filterExpression: null);
        return await ToPagedListAsync<TDomain>(
            query,
            pageNo,
            pageSize,
            cancellationToken);
    }

    // Count and Existence
    public virtual async Task<int> CountAsync(
        Expression<Func<TPersistence, bool>> filterExpression,
        CancellationToken cancellationToken = default)
    {
        return await QueryInternal(filterExpression).CountAsync(cancellationToken);
    }

    public virtual async Task<bool> AnyAsync(
        Expression<Func<TPersistence, bool>> filterExpression,
        CancellationToken cancellationToken = default)
    {
        return await QueryInternal(filterExpression).AnyAsync(cancellationToken);
    }

    protected virtual IQueryable<TPersistence> CreateQuery()
    {
        return GetSet();
    }

    protected virtual DbSet<TPersistence> GetSet()
    {
        return _dbContext.Set<TPersistence>();
    }
}

Entity Repository

For each domain entity, a specific repository is generated:
Customer Repository
public interface ICustomerRepository : IEFRepository<Customer, Customer>
{
    Task<Customer?> FindByIdAsync(Guid id, CancellationToken cancellationToken = default);
    Task<List<Customer>> FindByIdsAsync(Guid[] ids, CancellationToken cancellationToken = default);
}

public class CustomerRepository : RepositoryBase<Customer, Customer, ApplicationDbContext>, ICustomerRepository
{
    public CustomerRepository(ApplicationDbContext dbContext) : base(dbContext)
    {
    }

    public async Task<Customer?> FindByIdAsync(Guid id, CancellationToken cancellationToken = default)
    {
        return await FindAsync(x => x.Id == id, cancellationToken);
    }

    public async Task<List<Customer>> FindByIdsAsync(Guid[] ids, CancellationToken cancellationToken = default)
    {
        return await FindAllAsync(x => ids.Contains(x.Id), cancellationToken);
    }
}

Usage Examples

Basic CRUD Operations

CRUD Example
public class CustomerService
{
    private readonly ICustomerRepository _repository;

    public CustomerService(ICustomerRepository repository)
    {
        _repository = repository;
    }

    public async Task<Customer> CreateCustomerAsync(string name, string email)
    {
        var customer = new Customer(name, email);
        _repository.Add(customer);
        await _repository.UnitOfWork.SaveChangesAsync();
        return customer;
    }

    public async Task<Customer?> GetCustomerAsync(Guid id)
    {
        return await _repository.FindByIdAsync(id);
    }

    public async Task UpdateCustomerAsync(Guid id, string name)
    {
        var customer = await _repository.FindByIdAsync(id);
        if (customer != null)
        {
            customer.UpdateName(name);
            _repository.Update(customer);
            await _repository.UnitOfWork.SaveChangesAsync();
        }
    }

    public async Task DeleteCustomerAsync(Guid id)
    {
        var customer = await _repository.FindByIdAsync(id);
        if (customer != null)
        {
            _repository.Remove(customer);
            await _repository.UnitOfWork.SaveChangesAsync();
        }
    }
}

Advanced Queries

Query Examples
// Filter with expression
var activeCustomers = await _repository.FindAllAsync(
    x => x.IsActive && x.CreatedDate >= DateTime.UtcNow.AddMonths(-6)
);

// Paged results
var pagedCustomers = await _repository.FindAllAsync(
    pageNo: 1,
    pageSize: 20
);

// Custom query with includes
var customersWithOrders = await _repository.FindAllAsync(
    x => x.IsActive,
    query => query.Include(x => x.Orders).ThenInclude(o => o.Items)
);

// Count
var customerCount = await _repository.CountAsync(x => x.IsActive);

// Existence check
var emailExists = await _repository.AnyAsync(x => x.Email == "[email protected]");

Query Options

Query Options
// Using query options for complex queries
var result = await _repository.FindAllAsync(
    x => x.IsActive,
    query => query
        .Include(x => x.Orders)
        .Where(x => x.Orders.Any())
        .OrderByDescending(x => x.CreatedDate)
        .Take(10)
);

// Paged with query options
var pagedResult = await _repository.FindAllAsync(
    x => x.IsActive,
    pageNo: 1,
    pageSize: 20,
    queryOptions: query => query
        .Include(x => x.Orders)
        .OrderBy(x => x.Name)
);

Configuration Options

Adds the synchronous equivalents for repository asynchronous methods.When enabled, methods like Find(), FindAll(), Count() are generated alongside their async counterparts.Note: Synchronous methods are generally discouraged in modern applications. Use async methods when possible.Default: false

Stored Procedures

The module supports executing stored procedures through repositories:
Stored Procedure
public interface ICustomerRepository : IEFRepository<Customer, Customer>
{
    Task<List<CustomerReportDto>> GetCustomerReportAsync(DateTime fromDate, DateTime toDate);
}

public class CustomerRepository : RepositoryBase<Customer, Customer, ApplicationDbContext>, ICustomerRepository
{
    public async Task<List<CustomerReportDto>> GetCustomerReportAsync(
        DateTime fromDate, 
        DateTime toDate)
    {
        var fromDateParam = new SqlParameter("@FromDate", fromDate);
        var toDateParam = new SqlParameter("@ToDate", toDate);
        
        return await _dbContext
            .Set<CustomerReportDto>()
            .FromSqlRaw("EXEC sp_GetCustomerReport @FromDate, @ToDate", fromDateParam, toDateParam)
            .ToListAsync();
    }
}

Paging Support

The module includes a IPagedList<T> interface and implementation:
PagedList
public interface IPagedList<T>
{
    int TotalCount { get; }
    int PageCount { get; }
    int PageNo { get; }
    int PageSize { get; }
    IList<T> Items { get; }
}

public class PagedList<T> : IPagedList<T>
{
    public PagedList(int totalCount, int pageNo, int pageSize, IList<T> items)
    {
        TotalCount = totalCount;
        PageNo = pageNo;
        PageSize = pageSize;
        Items = items;
        PageCount = (int)Math.Ceiling(totalCount / (double)pageSize);
    }

    public int TotalCount { get; }
    public int PageCount { get; }
    public int PageNo { get; }
    public int PageSize { get; }
    public IList<T> Items { get; }
}

Data Contracts

When working with data contracts (DTOs mapped to database views or stored procedures), the module generates:
Data Contract Repository
public interface ICustomerSummaryRepository : IEFRepository<CustomerSummaryDto, CustomerSummaryDto>
{
    // Specialized query methods
}

public class CustomerSummaryRepository : RepositoryBase<CustomerSummaryDto, CustomerSummaryDto, ApplicationDbContext>, ICustomerSummaryRepository
{
    public CustomerSummaryRepository(ApplicationDbContext dbContext) : base(dbContext)
    {
    }
}

Dependency Injection

Repositories are automatically registered in the DI container:
Startup
services.AddScoped<ICustomerRepository, CustomerRepository>();
services.AddScoped<IOrderRepository, OrderRepository>();
// ... other repositories

Testing

Repositories can be easily mocked for unit testing:
Testing
public class CustomerServiceTests
{
    [Fact]
    public async Task CreateCustomer_ShouldAddToRepository()
    {
        // Arrange
        var mockRepository = new Mock<ICustomerRepository>();
        var mockUnitOfWork = new Mock<IUnitOfWork>();
        mockRepository.Setup(x => x.UnitOfWork).Returns(mockUnitOfWork.Object);
        
        var service = new CustomerService(mockRepository.Object);

        // Act
        await service.CreateCustomerAsync("John Doe", "[email protected]");

        // Assert
        mockRepository.Verify(x => x.Add(It.IsAny<Customer>()), Times.Once);
        mockUnitOfWork.Verify(x => x.SaveChangesAsync(default), Times.Once);
    }
}

Additional Resources

Build docs developers (and LLMs) love