C# DbContext with nested classes containing the Repository methods

centity-framework

So I have done a lot of reading and it appears that, since DbContext already implements a repository and unit of work pattern combination, creating more abstraction classes would be redundant. However, I would still like to be able to encapsulate commonly-used queries for code reuse. I was thinking – perhaps I may be able to store a query-container class that uses the current instance of DbContext to make sure that queries don't cross transaction boundaries. Take for instance, this dummy code:

public partial class PersonEntities : DbContext
{
    public PersonEntities() : base("name=SomeConnectionString")
    {
        this.Configuration.ProxyCreationEnabled = false;
        this.CustomersQuery = new CustomerQueries(this);
    }

    public virtual DbSet<Customer> Customers { get; set; }
    public virtual DbSet<Employees> Employees { get; set; }

    public CustomerQueries CustomersQuery { get; }

    public class CustomerQueries 
    { 
        protected PersonEntities Database { get; private set; }

        internal CustomerQueries(PersonEntities db)
        {
            this.Database = db;
        }

        public IEnumerable<Customer> GetCustomersByName(string firstName, string lastName, bool ignoreCase = true)
        {
            /// method implementation
        }
    }
}

You might use it like this (concrete classes and lack of factories for simplification):

        using (var context = new PersonEntities())
        {
            var query = context.CustomersQuery.GetCustomersByName("Alfred", "Morris", true);

            /// stuff

            await context.SaveChanges();
        }

Pros:

  1. Does not add additional abstraction
  2. Queries can use multiple DbSets at once within the same transaction, unlike with many of the repository (especially generic repository) implementatinos I've seen.
  3. Related queries are neatly contained in a single place with mostly only relevant methods (plus the common object methods like ToString), meanwhile DbSet class itself has tons of methods thanks to IEnumerable/IQueryable/etc extension methods and thus extending DbSet didn't seem like a good idea.
  4. It also becomes possible to make some generic queries, such as:
    GetPersonbyName<TPerson>(string firstName, stringLastName) where TPerson : Person, perhaps contained in a PersonsQuery class.

Cons

  1. Adds additional functionality that could be considered bloating, potentially leading to a 'god class' – however, these queries are still tightly coupled to the DbSets and are still in sub-classes so I'm not sure if this is the case.
  2. Perhaps others? I'm not really sure.

Are there any other drawbacks to using this style for code-reuse without redundant abstractions?

Best Answer

For this sort of thing I like to use C# extension methods against IQueryable<>. You would still have a class do define these but it wouldn't be nested in or referenced from your context.

Example:

public static class IQueryableOfCustomerExtensions
{
    public static IQuerable<Customer> GetByName(this IQueryable<Customer> customers, string firstName, string lastName, bool ignoreCase = true)
    {
        // Implementation
    }
}

You could then call this like:

var customers = context.Customers.GetByName("Alfred", "Morris", true).ToList();