Skip to content

wilsonsergio2500/efTurbo

Repository files navigation

EF Turpo Repo

GitHub Nuget Build

Simple and speedy but opinionated Repository pattern with query specification.

The idea

I envision creating a pattern that while not befitting for every use case, could aid in introducing an elegant abstraction while boilerplating at lighting speed.

The note worthy word here is “abstraction.” Emphasizing that this not a principle or library that would likely be brought into every project. However, if utilizing Entity Framework as a ORM you may find this approach useful, and subscribe to this implementation for the sake of simplicity while adhering to its ingredients.

Installation

dotnet add package EF.Turbo.Repo

Usage

For a given Context

public class CustomerContext: DbContext
{
    readonly string SCHEMA = "Customer";
    public CustomerContext(DbContextOptions<CustomerContext> options) : base(options)
    {
    }
    public virtual DbSet<Entities.Contact> Contacts { get; set; } = null!;
  ....
}

We could bring about a repository declaration like such:

using Customer.Repo.Core.Entities;
using EF.Turbo.Repo.Core.Interfaces;
using EF.Turbo.Repo.Core.Repo;

namespace Customer.Repo.Core.Repos
{
    public class ContactRepo : BaseRepo<CustomerContext, Contact>, IBaseRepo<CustomerContext, Contact>
    {
        public ContactRepo(CustomerContext dbContext) : base(dbContext)
        {
        }
    }
}

and This is all would ever be needed 😄 (If not extending the base repo, when seeking your own implementation) Subsequently, the repo could be inject as such

public static IServiceCollection AddCustomerRepository(this IServiceCollection services)
{
    services.AddScoped<IBaseRepo<CustomerContext, Contact>, ContactRepo>();
    ...
}

Any service could then consume the repository as such

public class ContactService : IContactService
{
	private readonly IBaseRepo<CustomerContext, Contact> _contactRepo;
	public ContactService(IBaseRepo<CustomerContext, Contact> contactRepo)
	{
		_contactRepo = contactRepo;
	}
	public async Task<Contact> GetContactByIdAsync(int id)
	{
		return await _contactRepo.GetByIdAsync(id);
	}
	...
}

Query Specification

As stated the library exposes a Query Specification pattern created by @ardalis. There are plenty of examples that could be found. However, In summary the specification allows for a query against the underlining Entity brought about by the generic type implementation.

using Ardalis.Specification;
using Customer.Repo.Core.Entities;

namespace Customer.Service.Core.Specs.Contacts
{
    public class ByEmail: Specification<Contact>
    {
        public ByEmail(string email)
        {
            Query.Where(contact => contact.EmailAddress == email);
        }
    }
    public class HasPhone: Specification<Contact>
    {
        public HasPhone()
        {
            Query.Where(contact => !string.IsNullOrEmpty(contact.PhoneNumber));
        }
    }
}
...

Hence We could have the following:

public async Task<bool> HasAnyContactWithEmail(string Email)
{
    return await contactRepo.AnyAsync(new ByEmail(Email));
}

public async Task<int> NumberOfContactsWithPhoneNumbers()
{
    return await contactRepo.CountAsync(new HasPhone());
}

Releases

No releases published

Packages

No packages published

Languages