I am creating a report to list people in my database, according to user-defined filter criteria. So, for example, I could filter by name, age etc.
var people = db.People.AsQueryable();
if (filterByName)
people = people.Where(p => p.LastName.Contains(nameFilter));
if (filterByAge)
people = people.Where(p => p.Age == age);
Now, one of the filter criteria is to show people who have not had their required immunizations. I have tables for Immunization and PersonImmunization (with a unique index on PersonID, ImmunizationID). If someone is missing any PersonImmunization records, or if number of doses they have received is under the requirement, they should be included, otherwise not.
If I were writing a SQL query, it would be:
select p.*
from Person p
cross join Immunization i
left join PersonImmunization pi
on pi.PersonID = p.ID and pi.ImmunizationID = i.ID
where pi.ID is null or pi.Doses < i.RequiredDoses;
Now in order to make this part of my where clause, I need to express this using an Expression predicate:
if (filterByImmunizations) {
Expression<Func<Person, bool>> nonCompliantImmunization =
person => <now what?>;
people = people.Where(nonCompliantImmunization);
}
The first problem I have is how to work Immunizations into the expression. Then, once I have it, I suspect that finding the non-compliant people might be more straightforward, but if you could include that in your answer, I'd much appreciate it!
EDIT: I've been requested to explain why I'm so set on getting a solution using an Expression<Func<Person, bool>>. The reason is because I have built a whole generic framework for writing complex, user-defined queries, in several different contexts. To give you an idea of what's inside the engine, here's a snippet of what's inside my base class:
public abstract class QueryBuilder<T> where T : EntityObject {
public static IQueryable<T> FilterQuery(IQueryable<T> query, IEnumerable<QueryConditionLite> filters, bool anyConditionSufficient) {
...
}
protected Expression<Func<TBase, bool>> GetPredicate(Expression<Func<TBase, double>> expression, IQueryCondition condition) {
...
}
}
Then I have a PersonQueryBuilder : QueryBuilder<Person>, and within that I want to create a filter that shows the people who are non-compliant with their immunization requirements. I think you will agree that query syntax just ain't gonna cut it.
I'd approach it as a multi-part join:
var nonCompiantImmunization =
from p in Persons
from i in Immunizations
let pi = PersonImmunizations.Where(x =>
x.ImmunizationID == i.ID && x.PersonID == p.ID)
where !pi.Any() || pi.Sum(x => x.Doses) < i.RequiredDoses
select new { p, i };
Edit: To make it fit the Expression<Func<Person, bool>> constraint, I suppose you could rephrase it as:
Expression<Func<Person, bool>> nonCompliantImmunization =
person => (
from i in Immunizations
let pi = PersonImmunizations.Where(x =>
x.ImmunizationID == i.ID && x.PersonID == person.ID)
where !pi.Any() || pi.Sum(x => x.Doses) < i.RequiredDoses
select true
).Any();
You should be able to write is like this instead:
var people = db.People.AsQueryable();
if(filterByImmunizations)
{
people = from p in people
from i in db.Immunization
from pi in db.PersonImmunization.Where(x =>
x.PersonID == p.ID && x.ImmunizationID == i.ID).DefaultIfEmpty()
where pi.ID == null || pi.Doses < i.RequiredDoses
select p;
}
Related
I have a Linq statement which has two optional Where clauses but I cannot work out how to implement this linq (at the moment I have 4 linq statements in this method which off course is not good!):
//Params passed into my method:
int StoreId = 0;
bool ShowEnabledTills = true;
var query = (from Transactions in db.Transactions
join Tills in db.Tills on new { TillId = Convert.ToInt32(Transactions.TillId) } equals new { TillId = Tills.TillId }
join CompanyAddresses in db.CompanyAddresses on new { CompanyId = Convert.ToInt32(Transactions.StoreID) } equals new { CompanyId = CompanyAddresses.CompanyId }
where
CompanyAddresses.CompanyId == StoreId <===== OPTIONAL
&& Tills.Active == ShowEnabledTills <===== OPTIONAL
select new
{
Tills.TillId,
Tills.ComputerName,
Tills.Description,
CompanyAddresses.CompDescription
}).Distinct();
I took a look at PredicateBuilder but I couldn't quite get my head around this but if I can create some re-usable code this would be great!
// get these from params to decide optional or not
var validateStoredId = false;
var validateActiveStatus = false;
And in your where clause do something like:
where
(!validateStoreId || CompanyAddresses.CompanyId == StoreId)
&& (!validateActiveStatus || Tills.Active == ShowEnabledTills)
I have a little to contribute here I think. I came across the same problem, and implemented a similar solution. HOWEVER! This wont short-circuit as you would hope (or at least it didn't in mine!)
This led to me looping through a list of 10k items checking if true == false effectively, which whilst not an expensive check is a check you don't want to do thousands of times nevertheless.
For anyone else coming to this, I'd recommend splitting your queries down into subqueries wrapped in if checks, that is far more performant when dealing with large datasets :)
Edit : I built this little helper for doing exactly this, hope someone else finds it useful, I enjoyed not having to break my method chains :)
public static IEnumerable<T> OptionalWhere<T>(this IEnumerable<T> source, Func<T, bool> predicate, bool excecuteFilter)
{
if (!excecuteFilter)
{
return source;
}
else
{
return source.Where(predicate);
}
}
Try this:
var query = db.Transactions.Join(
db.Tills, //Join to this table
transaction => transaction.TillId //Outer key
, till => till.TillId, //Inner key
(transaction, till) => new {transaction, till}) //Result of join
.Join(db.CompanyAddresses, //Join to this table
r => r.till.StoreId, //Outer key
a => a.CompanyId, //Inner key
(r, address) => new {r.transaction, r.till, address}); //Result of join
if(StoreId != null)
query = query.Where(d => d.address.StoreId == StoreId);
if(ShowEnabledTills)
query = query.Where(d => d.till.Active == true);
var items = query.Select(d => new {
d.Till.TillId,
d.Till.ComputerName,
d.Till.Description,
d.address.CompDescription
}).Distinct();
I did it without knowing much about your schema, but this should give you a good idea.
I disliked the "SQL style" syntax for this very reason (confusing at times). I started using method calls like this and never had a problem since.
I love to build LINQ to entity queries dynamically. For simple a simple filter I do something like this:
var query = model.Pets;
if(searchForName)
query = query.Where(p => p.Name == "Bello");
if(searchForType)
query = query.Where(p => p.Type == 1);
var result = query.ToList();
Now, I want exactly this type of dynamic query building for a more complex query.
This is the new (sample) schema:
Is there a way to generate a resulting query to select all Pets but only include Owner with some properties?
The results should be:
model.Pets.Include(p.Owner);
or
model.Pets.Include(p.Owner.Where(o => o.Name == "Hans"));
or
model.Pets.Include(p.Owner.Where(o => o.Gender == 0));
or
model.Pets.Include(p.Owner.Where(o => o.Name == "Hans").Where(o => o.Gender == 0));
I didn't find a solution to inject a Where clause in the middle of a query.
The above sample is simplified, I need this for more complex queries with joins and subselects.
Maybe you can use the query syntax (which is, in my opinion, much easier to use) :
List<Pets> petsList = (from pet in model.Pets
join owner in model.Owner on pet.Owner_name equals owner.Name
where owner.Name.Equals("Hans") || owner.Name.Equals("James")
where pet.Type == 1
select pet).ToList();
Hope this helps.
PS : for primary key, prefer integer instead of strings.
I would recomend you to user PredicateBuilder
please see the site http://www.albahari.com/nutshell/predicatebuilder.aspx
it'll be more easier to build your dynamic filters
you can even use methods
public static Expression<Func<Product, bool>> ContainsInDescription (
params string[] keywords)
{
var predicate = PredicateBuilder.False<Product>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or (p => p.Description.Contains (temp));
}
return predicate;
var classics = Product.ContainsInDescription ("Nokia", "Ericsson")
.And (Product.IsSelling());
The following Entity Framework query runs without error.
Predicate<Program> filterProgram;
if (programId.HasValue)
filterProgram = (p => p.Id == programId && !p.IsDeleted);
else
filterProgram = (p => !p.IsDeleted);
var analytics = (from a in repository.Query<Analytic>()
where (a.Marker == "Open" || a.Marker == "LastTouch") &&
a.EntityType == "Proposal" &&
a.Site == "C"
join p in repository.Query<Program>()
on a.EntityId equals p.Id
//where filterProgram(p)
group a
by new { a.LoginSessionId, a.EntityId, p.Id, p.Name } into g
let f = g.OrderBy(x => x.TimestampUtc).FirstOrDefault(x => x.Marker == "Open")
where f != null
let t = g.FirstOrDefault(x => x.Marker == "LastTouch" && x.TimestampUtc > f.TimestampUtc)
select new
{
ProgramId = g.Key.Id,
Program = g.Key.Name,
ProposalId = g.Key.EntityId,
FirstOpen = f,
LastTouch = (t ?? f).TimestampUtc
}).ToList();
However, if I uncomment the line where filterProgram(p), I get the run-time error:
The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
I was expecting that LINQ would be able to incorporate my predicate into the query and convert it to SQL. Why am I getting this error, and is there a way to dynamically modify a where predicate this way?
The problem is caused because Entity Framework needs to be able to convert your LINQ query into SQL. Your LINQ query is compiled into a data structure called an Expression tree which is then passed to Entity Framework for conversion to SQL.
You have two options:
Replace your call to filterProgram with a more basic C# expression. Depending on the complexity of filterProgram this may not be possible.
Remove you call to filterProgram, and convert this query to an IEnumerable<T>, maybe by calling .ToList(). You can then further filter the results of this query using filterProgram
Example of 2
var query = /* Your Query With filterProgram commented out */
var resultsFromSql = query.ToList();
var fullyFiltered = resultsFromSql.Select(filterProgram);
Change filterProgram's type to Expression<Func<Program, bool>> and then it should be usable in a LINQ Where clause.
One caveat, however: I managed to get it to work in method chain syntax, but not in query syntax.
For example, this works:
dataContext.Programs.Where (filterProgram)
but this does not:
from p in dataContext.Programs
where filterprogram
(The compiler complains that it cannot resolve method Where, which is available on both IEnumerable and IQueryable.)
In your case, it might be acceptable to replace the line
join p in repository.Query<Program>()
with
join p in repository.Query<Program>().Where(filterProgram)
In Linq to Entities it is not possible to call an external method without converting the query to IEnumerable. Thus, since filterProgram is a method, you may not call inside the query.
If possible you may call ToList before calling the FilterProgram and it will work.
Another option maybe inserting the logic of filterProgram into the query.
The problem is that linq2entities tries to translate the expression tree to SQL your expression tree has an invocation of the method Invoke on a delegate type (filterProgram)
however there's nothing stopping you from inlining that predicate
var id= programId.HasValue ? programId.GetValueOrDefault() : -1;
var analytics = (from a in repository.Query<Analytic>()
where (a.Marker == "Open" || a.Marker == "LastTouch") &&
a.EntityType == "Proposal" &&
a.Site == "C"
join p in repository.Query<Program>()
on a.EntityId equals p.Id
where !p.IsDeleted && (!hasValue || p.Id == id)
....
This assumes that -1 is an invalid programId
I have the following query in LINQ to Entities:
var query = from p in db.Products
where p.Price > 10M
select p;
At this point the query has not executed, and I want to write a query that will return true/false based on some conditions:
return query.Any(p => p.IsInStock &&
(p.Category == "Beverage" ||
p.Category == "Other"));
This works fine; however, I would like to get some reuse out of my code. I have many methods that need to filter based on if the category is a Beverage or Other, so I tried creating a delegate:
Func<Product, bool> eligibleForDiscount = (product) => product.Category == "Beverage" || product.Category == "Other";
I wanted to substitute the inline check with the delegate:
return query.Any(p => p.IsInStock && eligibleForDiscount(p));
This gives me an error saying that LINQ to Entities doesn't support Invoke. Why can't I substitute the inline code for a delegate like this, and is there any way I can accomplish my reuse some other way?
Recall that under the hood Linq-to-{DATABASE} is just transforming the IQueryable you've produced into Sql.
You can't inline code like that because an Invoke (the method you're actually calling when you call a Func or Action) has no consistent way to transform it into a sql statement (you could be doing anything in there).
That said you can reuse parts by splitting it up:
var query = from p in db.Products
where p.Price > 10M
select p;
query = query.Where(p => p.IsInStock);
query = query.Where(p => p.Category == "Beverage" || p.Category == "Other");
return query.Any();
Both those can be put into methods that take an IQueryable<Product> and return the same (but filtered). Then you can reuse to your heart's content!
The problem is that IQueryable needs to generate a SQL expression to pass to RDBMS, and it cannot do it when all it has is an opaque predicate.
The obvious but inefficient way is to rewrite your query as follows:
return query.Where(p => p.IsInStock).AsEnumerable().Any(eligibleForDiscount);
A less trivial way would be as follows:
bool GotEligible(Expression<Func<Product,bool>> pred) {
return query.Where(p => p.IsInStock).Any(pred);
}
Note how instead of a predicate this method takes a predicate expression. Now it is transparent to the EF, and can be converted to a SQL query without a problem.
As long as you stick with IQueryable, you can keep reusable querys in functions.
public IQueryable<Product> EligibleForDiscount(IQueryable<Product> products)
{
return products.Where(p => product.Category == "Beverage" ||
product.Category == "Other");
}
Now call it like any other function:
IQueryable<Product> query = (from p in db.Products
where p.Price > 10M
select p);
query = EligibleForDiscount(query);
How do you write a dynamic Linq query for the following simple search criteria?
1) StudentNumber
2) LastName
3) LastName and FirstName
//if (!String.IsNullOrEmpty(StudentNumber))
var results = (from s in Students
where s.StudentNumber == 1001
select s
);
//else if (!String.IsNullOrEmpty(LastName) & (String.IsNullOrEmpty(FirstName))
var results = (from s in Students
where s.LastName == "Tucker"
select s
);
//else if (!String.IsNullOrEmpty(LastName) & (!String.IsNullOrEmpty(FirstName))
var results = (from s in Students
where s.LastName == "Tucker" && s.FirstName == "Ron"
select s
);
You need to declare your results variable outside of any individual query. This will allow you append different filters based upon your varying criteria, and append as many filters as you need. An example:
var results = Students.AsEnumerable(); // use .AsQueryable() for EF or Linq-to-SQL
if (!string.IsNullorEmpty(StudentNumber))
{
results = results.Where(s => s.StudentNumber.Equals(StudentNumber));
}
else if (!string.IsNullOrEmpty(LastName))
{
results = results.Where(s => s.LastName.Equals(LastName));
if (!string.IsNullOrEmpty(FirstName))
{
results = results.Where(s => s.FirstName.Equals(FirstName));
// filter is in addition to predicate against LastName
}
}
// results can be used here
If dealing with Linq-to-Entities or -Sql, type the initial query with Students.AsQueryable(); so that the filtering happens at the database rather than inside the application.
Is there a way I can construct the WHERE clause first and use it in a
Linq query without if...else
If you want to build the entire where before the first step of the query, it's the same logic. You are conditionally building the predicate, so you will have some sort of if/else involved. However, to build the entire predicate first, you could build against a Func<Student, bool> for Linq to Objects.
Func<Student, bool> predicate;
if (!string.IsNullOrEmpty(StudentNumber))
{
predicate = s => s.StudentNumber.Equals(StudentNumber);
}
else if (!string.IsNullOrEmpty(LastName))
{
predicate = s => s.LastName.Equals(LastName);
if (!string.IsNullOrEmpty(FirstName))
{
Func<Student, bool> p = predicate;
predicate = s => p(s) && s.FirstName.Equals(FirstName);
}
}
else
{
predicate = s => true;
}
var query = Students.Where(predicate);
You'll notice it's the exact same if/else structure. You could collapse that down into a complicated conditional expression
Func<Student, bool> predicate;
predicate = s =>
!string.IsNullOrEmpty(StudentNumber)
? s.StudentNumber.Equals(StudentNumber)
: !string.IsNullOrEmpty(LastName)
? !string.IsNullOrEmpty(FirstName)
? s.LastName.Equals(LastName) && s.FirstName.Equals(FirstName)
: s.LastName.Equals(LastName)
: true;
var query = Students.Where(predicate);
But I find that pretty well difficult to follow, certainly as compared to the longer if/else. This predicate is also bigger than the one we build via the if/else, because this one contains all the logic, it's not just the logic we conditionally added.