Created
April 24, 2019 03:01
-
-
Save seangwright/31d1d466d079e4c14f82b1d8d68f509b to your computer and use it in GitHub Desktop.
QueryableBuilder / Transformer pattern for easy query re-use behind a CQRS abstraction layer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Interfaces | |
public interface IQueryableBuilder<TReturn> | |
{ | |
/* | |
We require the context to be provided via method to | |
make it obvious this type works with persistence | |
If this was provided through DI then it wouldn't be as obvious | |
if this type were accidentally used in a layer beyond the persistence layer | |
*/ | |
IQueryable<TReturn> Build(IDbContext context); | |
} | |
public interface IQueryableTransformer<TExisting, TTransformed> | |
{ | |
IQueryable<TTransformed> Transform(IQueryable<TExisting> existingQuery); | |
} | |
public static class RecordQueryExtensions | |
{ | |
public static IQueryable<TTransformed> AddTransformer<TExisting, TTransformed>( | |
this IQueryable<TExisting> existingQuery, | |
IQueryableTransformer<TExisting, TTransformed> additionalQuery) => | |
additionalQuery.Transform(existingQuery); | |
} | |
/// Example implementations (these pieces can be built up in different combinations and re-used throughout the persistence layer | |
public class CustomerActiveQueryableBuilder : IQueryableBuilder<Customer> | |
{ | |
private readonly int customerId; | |
public CustomerQueryableBuilder(int customerId) => this.customerId = customerid; | |
public IQueryable<Customer> Build(IDbContext context) => | |
context | |
.Customers | |
.Where(c => c.Id == customerId) | |
.Where(c => c.IsActive); | |
} | |
public class CustomerActiveOrdersQueryableTransformer : IQueryableTransformer<Customer, Order> | |
{ | |
private readonly IDbContext context; | |
public CustomerActiveOrdersQueryableTransformer(IDbContext context) => this.context = context; | |
IQueryable<Order> Transform(IQueryable<Customer> existingQuery) => | |
existingQuery | |
.Join(context.Orders, | |
c => c.Id, | |
o => o.CustomerId, | |
(customer, order) => order); | |
} | |
public class DiscountedOrdersQueryableTransformer : IQueryableTransformer<Order, Order> | |
{ | |
IQueryable<Order> Transform(IQueryable<Order> existingQuery) => | |
existingQuery | |
.Where(o => o.Total > 10000.0); | |
} | |
/// Example use | |
public class CustomerExpensiveOrdersQueryHandler | |
{ | |
private readonly IDbContext context; | |
public CustomerExpensiveOrdersQueryHandler(IDbContext context) => this.context = context; | |
public Task<IEnumerbale<Order>> Handle(CustomerExpensiveOrderQuery query) | |
{ | |
int customerId = query.customerId; | |
var builder = new CustomerActiveQueryableBuilder(customerId); | |
return builder | |
.Build(context) | |
.AddTransformer(new CustomerActiveOrdersQueryableTransformer(context)) | |
.AddTransformer(new DiscountedOrdersQueryableTransformer()) | |
.ToListAsync(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment