Skip to content

Adding Custom Queries to Repository

Long Le edited this page Sep 20, 2017 · 3 revisions

Add extension method for IRepository<TEntity> or (if async) IRepostioryAsync<TEntity>
*Note: all things returned from Repository layer are of type TEntity or IEnumerable<TEntity> and not IQueryable<TEntity>, addressing compartmentalization concern that all queries are actually happening in the Repository layer.

public static class CustomerRepository
{
    public static decimal GetCustomerOrderTotalByYear(
        this IRepository<Customer> repository,
        string customerId, int year)
    {
        return repository
            .Find(customerId)
            .Orders.SelectMany(o => o.OrderDetails)
            .Select(o => o.Quantity*o.UnitPrice)
            .Sum();
    }

    // Custom query using IQueryable
    public static IEnumerable<Customer> CustomersByCompany(
        this IRepositoryAsync<Customer> repository,
        string companyName)
    {
        return repository
            .Queryable()
            .Where(x => x.CompanyName.Contains(companyName))
            .AsEnumerable();
    }

    public static IEnumerable<CustomerOrder> GetCustomerOrder(
        this IRepository<Customer> repository, 
        string country)
    {
        var customers = repository.GetRepository<Customer>().Queryable();
        var orders = repository.GetRepository<Order>().Queryable();

        var query = from c in customers
            join o in orders on new {a = c.CustomerID, b = c.Country}
                equals new {a = o.CustomerID, b = country}
            select new CustomerOrder
            {
                CustomerId = c.CustomerID,
                ContactName = c.ContactName,
                OrderId = o.OrderID,
                OrderDate = o.OrderDate
            };

        return query.AsEnumerable();
    }
}

Best practice, expose this through your service layer

/// <summary>
///     Add any custom business logic (methods) here
/// </summary>
public interface ICustomerService : IService<Customer>
{
    decimal CustomerOrderTotalByYear(string customerId, int year);
    IEnumerable<Customer> CustomersByCompany(string companyName);
    IEnumerable<CustomerOrder> GetCustomerOrder(string country);
}

/// <summary>
///     All methods that are exposed from Repository in Service are overridable to add business logic,
///     business logic should be in the Service layer and not in repository for separation of concerns.
/// </summary>
public class CustomerService : Service<Customer>, ICustomerService
{
    private readonly IRepositoryAsync<Customer> _repository;

    public CustomerService(IRepositoryAsync<Customer> repository) : base(repository)
    {
        _repository = repository;
    }

    public decimal CustomerOrderTotalByYear(string customerId, int year)
    {
        // add business logic here
        return _repository.GetCustomerOrderTotalByYear(customerId, year);
    }

    public IEnumerable<Customer> CustomersByCompany(string companyName)
    {
        // add business logic here
        return _repository.CustomersByCompany(companyName);
    }

    public IEnumerable<CustomerOrder> GetCustomerOrder(string country)
    {
        // add business logic here
        return _repository.GetCustomerOrder(country);
    }

    public override void Insert(Customer entity)
    {
        // e.g. add business logic here before inserting
        base.Insert(entity);
    }

    public override void Delete(object id)
    {
        // e.g. add business logic here before deleting
        base.Delete(id);
    }
}

Example in Web Api

public class CustomerController : WebApi
{
    private readonly ICustomerService _customerService;

    public CustomerController(ICustomerService customerService)
    {
        _customerService = customerService;
    }

    public HttpResponseMessage CustomerOrderTotalByYear(string customerId, int year)
    {
        var total = _customerService.CustomerOrderTotalByYear(customerId, year);
        return Request.CreateResponse(HttpStatusCode.OK, total);
    }
}