1

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?

1 Answer 1

1

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();
5
  • 1
    The advantage of returning IQueryable here is you aren't hitting the database right away. You can still leverage Entity Framework's lazy loading and delayed querying. As soon as you convert it to a list, you send that SQL off the the database. If you really wanted to restrict the return value so you can't add conditions, have it return IEnumerable<Customer> instead. Sep 16, 2015 at 12:27
  • That is a very valid point. It will allow you to compose queries queries. Given the scenario its perfectly acceptable to not materialise. I'll update the question.
    – Jason
    Sep 16, 2015 at 12:47
  • Yes but then you can't reference other DbSets with a generic extension method. You also wind up having to sort through tons of IQueryable extension methods to find the actual newly defined DB methods, as opposed to having a list of -only- the relevant methods (plus object methods). I would like to avoid both of those constraints.
    – NotJehov
    Sep 16, 2015 at 14:06
  • If you need to access other DbSets then you are correct this wouldn't work although I can't think of a scenario where you would need to do that over making use of navigation properties; if you have a use-case for it though then that's fair enough :) I would argue that the sorting through methods issue exists in both cases, it's just a matter of organising your code in such a way that makes it apparent.
    – Jason
    Sep 16, 2015 at 14:10
  • IQueryable contains a very large number of extension methods though, assuming there is a reference to Linq (which is common); the object class itself provides 4 public methods, and these are the only ones that pollute the list of methods for the query-container. DbSet contains even more methods with and without Linq referenced. However, you are correct that navigation properties can really help with this aspect. I will consider it.
    – NotJehov
    Sep 16, 2015 at 15:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.