1
\$\begingroup\$

I am using a CQRS type patter for querying data in my application. I am currently using entity framework to connect to the database.

public class GetCustomerQuery : IQuery<Customer>
{
     public string CustomerId { get; set; }
}

public class GetCustomerQueryHandler : IQueryHandler<GetCustomerQuery, Customer>
{
    public void Handle(GetCustomerQuery query)
    {
        return _dbContext.Customers.FirstOrDefault(c => c.Id == query.CustomerId);
    }
}

The problem I am having with this design is that the Customer object has relationships with other entities:

public void Customer
{
    public string Id { get; set; }
    public IList<Post> Posts { get; set; }
}

The query handler shown above will not return the list of posts, as this is a one-to-many relationship in the database. The way I was thinking to solve this was to add a BaseQuery object which allowed settings includes:

public abstract class BaseQuery<T> : IQuery<T>
{
    public void AddInclude(Expression<Func<T, object>> include)
    {
      ....
    }
}

Is this considered a leaky abstraction, as it exposes the structure of the database?

Is there a better way around this problem?

| improve this question | | | | |
\$\endgroup\$
  • \$\begingroup\$ Does Posts need to be an IList? IIRC if you make it something that can be lazy-loaded, you can avoid the problem that way \$\endgroup\$ – Ben Aaronson Apr 14 '15 at 12:27
  • \$\begingroup\$ @Ben The data is returned to the client via a web service. So the object is disconnected from the DbContext, which prevents lazy loading. \$\endgroup\$ – Nick Williams Apr 14 '15 at 12:33
1
\$\begingroup\$

I don't think you should add a method for setting includes on the query object. That means your consumers have to know which properties need to be included and which ones don't.

I think you should be explicit about it on the query. One approach could be to simply add a property:

// Slightly odd use of "WithPosts" to make it more comparable.
public class GetCustomerQuery : IQuery<Customer>
{
    public string CustomerId { get; private set; }

    public bool IncludePosts { get; private set; }

    public GetCustomerQuery(string customerId) 
    {
        CustomerId = customerId;
    }

    public GetCustomerQuery WithPosts()
    {
        return new GetCustomerQuery(CustomerId) { IncludePosts = true};
    }
}

You could also leverage some inheritance:

public class GetCustomerWithPostsQuery : GetCustomerQuery
{
    public GetCustomerWithPostsQuery(string customerId) : base(customerId)
    {
    }
}

public class GetCustomerQuery : IQuery<Customer>
{
    public string CustomerId { get; private set; }

    public GetCustomerQuery(string customerId) 
    {
        CustomerId = customerId;
    }

    public GetCustomerWithPostsQuery WithPosts()
    {
        return new GetCustomerWithPostsQuery(CustomerId);
    }
}

Either way:

var customerQueryWithoutPosts = new CustomerQuery("Dennis");
var customerQueryIncludingPosts = new CustomerQuery("Dennis").WithPosts();
// ... profit?

You can then either check the query's IncludePosts property OR have a different handler for the specialised GetCustomerWithPostsQuery query object.

| improve this answer | | | | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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