Skip to content

Instantly share code, notes, and snippets.

@seangwright
Created April 24, 2019 03:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seangwright/31d1d466d079e4c14f82b1d8d68f509b to your computer and use it in GitHub Desktop.
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
/// 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