Skip to content

Encapsulating Query Logic with the Query Object Pattern (Reusable Queries)

Long Le edited this page Sep 20, 2017 · 5 revisions

Examples done in LINQPad

Northwind.Repository.Queries.OrderSalesQuery.cs Simple Query Object

public class OrderSalesQuery : QueryObject<Order>
{
    public decimal Amount { get; set; }
    public string Country { get; set; }
    public DateTime FromDate { get; set; }
    public DateTime ToDate { get; set; }

    public override Expression<Func<Order, bool>> Query()
    {
        return (x => 
            x.OrderDetails.Sum(y => y.UnitPrice) > Amount &&
            x.OrderDate >= FromDate &&
            x.OrderDate <= ToDate &&
            x.ShipCountry == Country);
    }
}

Querying with the OrderSalesQuery.cs

var orderRepository = new Repository<Order>(this);
	
var orders = orderRepository
	.Query(new OrderSalesQuery(){ 
		Amount = 100, 
		Country = "USA",
		FromDate = DateTime.Parse("01/01/1996"), 
		ToDate = DateTime.Parse("12/31/1996" )
	})
	.Select();

Northwind.Repository.Queries.CustomerLogisticsQuery.cs Fluent Query Object

public class CustomerLogisticsQuery : QueryObject<Customer>
{
    public CustomerLogisticsQuery FromCountry(string country)
    {
        Add(x => x.Country == country);
        return this;
    }

    public CustomerLogisticsQuery LivesInCity(string city)
    {   
        Add(x => x.City == city);
        return this;
    }
}

Northwind.Repository.Queries.CustomerSalesQuery.cs Fluent Query Object

public class CustomerSalesQuery : QueryObject<Customer>
{
    public CustomerSalesQuery WithPurchasesMoreThan(decimal amount)
    {
        Add(x => x.Orders
            .SelectMany(y => y.OrderDetails)
            .Sum(z => z.UnitPrice * z.Quantity) > amount);

        return this;
    }

    public CustomerSalesQuery WithQuantitiesMoreThan(decimal quantity)
    {
        Add(x => x.Orders
            .SelectMany(y => y.OrderDetails)
            .Sum(z => z.Quantity) > quantity);

        return this;
    }
}

Using the reusable CustomerLogisticsQuery and CustomerSalesQuery together by chaining them together.

var customerRepository = new Repository<Customer>(this);

var query1 = new CustomerLogisticsQuery()
	.LivesInCity("London");

var query2 = new CustomerSalesQuery()
	.WithPurchasesMoreThan(100)
	.WithQuantitiesMoreThan(10);

customerRepository
	.Query(query1.And(query2))
	.Select()
	.Dump();