Reputation: 1871
I have a fairly complex query I need to write. I am hoping to do it using Linq to Sql if possible. The database is something like this:
Customers(First, Last, ID, Gender)
Orders (Date, Quantity, Weight, ItemName, Price)
Address (City, State, Zip)
The query would be to let a user search by any of these fields, and in the case of the number fields, to search <, =, or > as they desire.
Something like this would be sample query that I'm required to implement:
Query 1: Select customers where first name = 'John' and have at least one order with (Weight > 40 OR Quantity > 10 OR Price > 5) and zipcode = 12345.
Query 2: Select customers where first name = 'John' and have at least one order with Weight < 20 and ItemName = 'widget' and Quantity = 10) and zipcode = 12345.
I can get the basic part of searching for customers, but I am stuck on searching the Order table where the user can specify <>= in an OR manner.
query = Context.Customers.AsQueryable();
if (searchingFirstName) query = query.Where(cust => cust.First == firstName);
if (searchingLastName) query = query.Where(cust => cust.Last == lastName);
if (searchingZip) query = query.Where(cust => cust.Address.Zip == zip);
// using dynamic Linq
if (searchingGender) query = query.Where("Gender == @0", gender);
// how do I search the Orders? The dynamic linq functions appear
// to only work on the top level table
Upvotes: 0
Views: 1147
Reputation: 89231
You could use PredicateBuilder from LinqKit. It adds some new extension methods to predicate lambdas:
var predicate = PredicateBuilder.True<Customer>();
if (searchingFirstName)
{
predicate = predicate.And(cust => cust.First == firstName);
}
if (searchingOrders)
{
// Some code to unify the .And() and .Or() cases
Expression<Func<Order, bool>> subpredicate;
Func<Expression<Func<Order, bool>>, Expression<Func<Order, bool>>, Expression<Func<Order, bool>>> joiner;
if (orderMethodAny)
{
subpredicate = PredicateBuilder.True<Order>();
joiner = PredicateBuilder.And;
}
else
{
subpredicate = PredicateBuilder.False<Order>();
joiner = PredicateBuilder.Or;
}
if (searchingOrderDate)
{
// ...
}
if (searchingOrderWeight)
{
switch (orderOp)
{
case Op.Less:
subpredicate = joiner(subpredicate, ord => ord.Weight < orderWeight);
break;
case Op.LessEqual:
subpredicate = joiner(subpredicate, ord => ord.Weight <= orderWeight);
break;
case Op.Equal:
subpredicate = joiner(subpredicate, ord => ord.Weight == orderWeight);
break;
case Op.GreaterEqual:
subpredicate = joiner(subpredicate, ord => ord.Weight >= orderWeight);
break;
case Op.Greater:
subpredicate = joiner(subpredicate, ord => ord.Weight > orderWeight);
break;
case Op.NotEqual:
subpredicate = joiner(subpredicate, ord => ord.Weight != orderWeight);
break;
}
}
if (searchingOrderQuantity)
{
// ...
}
if (searchingOrderItemName)
{
// ...
}
if (searchingOrderPrice)
{
// ...
}
predicate = predicate.And(cust => cust.Orders.Any(subpredicate));
}
if (searchingZipCode)
{
predicate = predicate.And(cust => cust.ZipCode == zipCode);
}
var query = Context.Customers.Where(predicate);
You might need to call .Expand()
on the predicates before passing them as arguments, or .AsExpandable()
on the queryable, if you are using the Entity Framework.
Upvotes: 1