Conditional Where in Linq using query syntax - c#

Is there a better way to have conditional where clauses in LINQ equivalent to the following?
var doGroup=false;
var doSub=true;
var query= from mydata in Data_Details
where
((doGroup && mydata.Group == "xxxx") || (doGroup==false)) &&
((doSub && mydata.SubGroup == "yyyy") || (doSub==false))
select mydata;
In the code above that works it will optionally include 'Group' and 'SubGroup' depending on whether doGroup and doSub are true are false.
I know when using method syntax you can simply keep appending code to the query in separate lines but I'd prefer to keep using query syntax.

The easiest way to make this smaller is by optimizing the conditions themselves:
var doGroup=false;
var doSub=true;
var query=from mydata in Data_Details
where
(!doGroup || mydata.Group == "xxxx") &&
(!doSub || mydata.SubGroup == "yyyy")
select mydata;

You can write an extension method:
public static IQueryable<T> WhereIf<T>(
this IQueryable<T> source, bool condition,
Expression<Func<T, bool>> predicate)
{
if (condition)
return source.Where(predicate);
else
return source;
}
Usage:
var doGroup =false;
var doSub = true;
var query = Data_Details
.WhereIf(doGroup, q => q.Group == "xxxx")
.WhereIf(doSub, e => e.SubGroup == "yyyy")

Ruud Kobes answer is the right one,
a small point at which there is not need to project data,
last select is not needed.
var doGroup=false;
var doSub=true;
var query=from mydata in Data_Details
where
(!doGroup || mydata.Group == "xxxx") &&
(!doSub || mydata.SubGroup == "yyyy");

Related

Error while re-using an expression using PredicateBuilder in LINQ

I am trying to re-use an expression in a LINQ query but I get an exception saying "Code supposed to be unreachable". I'm not very much versed with PredicateBuilder. So pardon me if I look stupid somewhere. Below are the code snippets.
The expression that I am building via PredicateBuilder for re-use:
private static Expression <Func <DiscussionTopic , bool >> TopicExpr(Guid topicId, TaskMember taskMember, bool invert = false)
{
var predicate = PredicateBuilder.True<DiscussionTopic >();
predicate = predicate.And(topic => topic.TopicType == TopicType.Topic && topic.DiscussionTopicId == topicId);
predicate = predicate.And(ExpressionStore.IsTopicAnsweredExpression(taskMember.PersonId));
predicate = predicate.And(topic => (topic.TopicGroup == null || topic.TopicGroup.TopicVisibility == TopicVisibilityType .AllParticipants ||
(topic.TopicGroup.TopicVisibilityTags.Any(tv => taskMember.Person.PeopleTags.Any(pt => pt.ContentTagId == tv.ContentTagId) || taskMember.TaskMemberTags.Any(tmt => tmt.ContentTagId == tv.ContentTagId)))));
predicate = predicate.And(topic => (topic.TopicVisibility == TopicVisibilityType.AllParticipants ||
(topic.TopicVisibilityTags.Any(tv => taskMember.Person.PeopleTags.Any(pt => pt.ContentTagId == tv.ContentTagId) || taskMember.TaskMemberTags.Any(tmt => tmt.ContentTagId == tv.ContentTagId)))));
return predicate;
}
Next, I am trying to use the above expression in a nested query as below:
public static IQueryable <TaskMember > ApplyTopicsCompletedFilter(this IQueryable<TaskMember > input, Guid[] topicIds, bool invert = false)
{
var predicate = PredicateBuilder.True<TaskMember >();
predicate = predicate.And(taskMember => taskMember.TaskMembership.Discussions.Any(discussion => topicIds.All(topicId =>
discussion.DiscussionTopics.AsQueryable().Any(TopicExpr(topicId, taskMember, invert)))));
input = input.Where(predicate);
return input;
}
Some points to mention which are skipped for brevity:
The ApplyTopicsCompletedFilter function is an extension method that receives an already constructed query as input by the caller. Therefore, AsExpandable() operator is applied in the caller and not here.
ExpressionStore.IsTopicAnsweredExpression in line 5 in TopicExpr is an external query that has a complex logic for checking if a member has answered a topic in a discussion. I have skipped the definition here but it's a normal expression with signature Expression<Func<DiscussionTopic, bool> returned by a function that takes a PersonId as a parameter.
If I remove AsQueryable() from line 4 in ApplyTopicsCompletedFilter function, I receive internal data error. Of course if I do that, I also have to do TopicExpr(....).Compile() in the same line otherwise the code won't compile.
Please help as I have wasted almost a day trying to find a nice way of sharing business logic across different functions in our application. Our code is quite complex and uses a lot of sub-queries as well. That's why I am looking for a way where I can share a piece of logic no matter if it appears in a parent query or a sub-query inside a parent query.
Edit:
I just commented everything inside TopicExpr and now it just looks like this:
private static Expression<Func<DiscussionTopic, bool>> TopicExpr(Guid topicId, TaskMember taskMember, bool invert = false)
{
var predicate = PredicateBuilder.True<DiscussionTopic>();
predicate = predicate.And(topic => topic.TopicType == TopicType.Topic && topic.DiscussionTopicId == topicId);
return predicate;
}
I have done this to make the query simple to test. Here is the complete flow now:
[Route("api/accounts/testPredicate")]
[HttpGet]
public IQueryable<apiPerson> TestPredicate()
{
return this.TestPredicate(this.CurrentAccountId, new[] { MembershipStatusType.Member }, new apiFilterData { TopicsCompleted = new[] { Guid.Parse("<some guid>") } }).Select(tm => tm.Person).ToApiObjects();
}
public IQueryable<TaskMember> TestPredicate(Guid currentAccountId, MembershipStatusType[] statusTypes = null, apiFilterData filterData = null)
{
var query = this.Db.Set<TaskMember>().Where(t => t.Person.AccountId == currentAccountId).AsExpandable();
if (statusTypes != null)
query = query.Where(t => statusTypes.Contains(t.Status));
if (filterData.TopicsCompleted != null && filterData.TopicsCompleted.Length > 0)
query = query.ApplyTopicsCompletedFilterWithPredicate(filterData.TopicsCompleted);
if (filterData.TopicsNotCompleted != null && filterData.TopicsNotCompleted.Length > 0)
query = query.ApplyTopicsCompletedFilterWithPredicate(filterData.TopicsNotCompleted, true);
return query;
}
private static Expression<Func<DiscussionTopic, bool>> TopicExpr(Guid topicId, TaskMember taskMember, bool invert = false)
{
var predicate = PredicateBuilder.True<DiscussionTopic>();
predicate = predicate.And(topic => topic.TopicType == TopicType.Topic && topic.DiscussionTopicId == topicId);
return predicate;
}
public static IQueryable<TaskMember> ApplyTopicsCompletedFilterWithPredicate(this IQueryable<TaskMember> input, Guid[] topicIds, bool invert = false)
{
var predicate = PredicateBuilder.True<TaskMember>();
predicate = predicate.And(taskMember => taskMember.TaskMembership.Discussions.AsQueryable().Any(discussion => topicIds.All(topicId =>
discussion.DiscussionTopics.AsQueryable().Any(TopicExpr(topicId, taskMember, invert)))));
input = input.Where(predicate);
return input;
}
public static IQueryable<apiTopicSimple> ToApiObjects(this IQueryable<DiscussionTopic> items, Guid currentUserId, MembershipStatusType membershipStatus, bool stagingSite)
{
return from t in items
let ms = membershipStatus
let uId = currentUserId
let sS = stagingSite
select new apiTopicSimple
{
//Property initialization
};
}
PS: These functions are in different files inside the project but I have put them here at one place for definition purposes so that someone can go through the complete flow.

Creating a composite condition using anonymous filter method

I am trying to edit a search tool using linq,
What I like a filter in where clause is (ItemNumber == X AND ( StatementStatus == SatusA Or StatementStatus == StatusB ) )
But right now, it is like:
What I like a filter in where clause is (ItemNumber == X AND StatementStatus == SatusA Or StatementStatus == StatusB )
as AND has higher operational priority over OR the result is not what I want. :)
Could you please help?
using (var ctx = new MyContext()) {
Func<Statement, bool> filter = null;
if (!string.IsNullOrEmpty(request.ItemNumber))
filter = new Func<Statement, bool>(s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber));
if (request.StatusA)
filter = filter == null ? new Func<Statement, bool>(s => s.StatementStatus == StatementStatusType.StatusA) :
filter.And(s => s.StatementStatus == StatementStatusType.StatusA);
if (request.StatusB)
filter = filter == null ? new Func<Statement, bool>(s => s.StatementStatus == StatementStatusType.StatusB) :
filter.Or(s => s.StatementStatus == StatementStatusType.StatusB);
var results = ctx.Statements
.Include("StatementDetails")
.Include("StatementDetails.Entry")
.Where(filter)
.Take(100)
.Select(s => new StatementSearchResultDTO{ ....
}
}
That's happens not because AND have higher priority than OR. What happens in reality:
var firstFilter = ...; // itemNumber
var secondFilter = ...; // statusA
var firstAndSecondFilter = firstFilter.And(secondFilter); // itemNumber && statusA
var thirdFilter = ...; // statusB
var endFilter = firstAndSecondFilter.Or(thirdFilter) // (itemNumber && statusA) || statusB.
The problem - wrong control flow. You must to do something like that:
var filterByA = ...;
var filterByB = ...;
var filterByAorB = filterByA.Or(filterByB);
var filterByNumber = ...;
var endFiler = filterByNumber.And(filterByAorB);
And your code is bad, not just because it works wrong, but because it's hard to write code in such style. Reasons:
This code doesn't follow DRY principle. You have two same lambdas that checks for StatusA (look in your ternary operator) and two same lambdas that checks for StatusB
You have too long ternary operator with null checks. That's bad because you don't see general picture, your eyes focused on syntax problems. You may write and extension method AndNullable for funcs. Like this:
static Func<T1, TOut> AndNullable<T1, TOut>(this Func<T1, TOut> firstFunc, Func<T1, TOut> secondFunc) {
if (firstFunc != null) {
if (secondFunc != null)
return firstFunc.And(secondFunc);
else
return firstFunc;
}
else {
if (secondFunc != null)
return secondFunc;
else
return null;
}
}
And that same for Or. Now your code can be wroted like this:
Func<Statement, bool> filter = null;
if (request.StatusA)
filter = s => s.StatementStatus == StatementStatusType.StatusA;
if (request.StatusB)
filter = filter.OrNullable(s => s.StatementStatus == StatementStatusType.StatusB);
if (!string.IsNullOrEmpty(request.ItemNumber))
filter = filter.AndNullable(s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber));
Reads more better.
Your filter is global filter. Writing of global filter is simpler for few filter conditions and number of lines is small, but it's more complicated to understand your filter. Rewrite it in this way:
Func<Statement, bool> filterByStatusA = null;
Func<Statement, bool> filterByStatusB = null;
if (request.StatusA)
filterByStatusA = s => s.StatementStatus == StatementStatusType.StatusA;
if (request.StatusB)
filterByStatusB = s => s.StatementStatus == StatementStatusType.StatusB;
Func<Statement, bool> filterByStatuses = filterByStatusA.OrNullable(filterByStatusB);
Func<Statement, bool> filterByItemNumber = null;
if (!string.IsNullOrEmpty(request.ItemNumber))
filterByItemNumber = s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber);
Func<Statement, bool> endFilter = filterByItemNumber.And(filterByStatuses);
Okay, we have outthinked how we can write filters by combining them as Func<..> but we still have problems.
What problems we will got, if result filter is null? Answer: ArgumentNullException due to documentation. We must to think about this case.
What another problems we can got with using of simple Func<...>? Well, you must to know difference between IEnumerable<T> and IQueryable<T> interfaces. In simple words, all operations on IEnumerable causes simple iteratation over all elements (well, it's lazy, IEnumerable really slower than IQueryable). So, for example, combining of Where(filter), Take(100), ToList() on collection that have 10000 elements that are bad for this filter and 400 elements that are good will cause iterating over 10100 elements. If you wrote similar code for IQueryable the request of filtering will send on database server and this server will iterate only ~400 (or 1000, but not 10100), if you have configured indexes on database. So what happens in your code.
var results = ctx.Statements // you are getting DbSet<Statement> that implements interface IQueryable<Statement> (and IQueryable<T> implements IEnumerable<T>)
.Include("StatementDetails") // still IQueryable<Statement>
.Include("StatementDetails.Entry") // still IQueryable<Statement>
.Where(filter) // Cuz your filter is Func<..> and there are no extension methods on IQueryable that accepts Func<...> as parameter, your IQueryable<Statement> casted automatically to IEnumerable<Statement>. Full collection will be loaded in your memory and only then filtered. That's bad
.Take(100) // IEnumerable<Statement>
.Select(s => new StatementSearchResultDTO { .... // IEnumerable<Statement> -> IEnumerable<StatementSearchResultDTO>
}
Okay. Now you understand the problem. So, simple right code for you can be writed in this way:
using (var ctx = new MyContext()) {
results = ctx.Statements
.Include("StatementDetails")
.Include("StatementDetails.Entry")
.AsQueryable();
if (!string.IsNullOrEmpty(request.ItemNumber))
results = results.Where(s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber));
if (request.StatusA) {
if (request.StatusB)
results = results.Where(s => s.StatementStatus == StatementStatusType.StatusA ||
s.StatementStatus == StatementStatusType.StatusA);
else
results = results.Where(s => s.StatementStatus == StatementStatusType.StatusA);
}
else {
if (request.StatusB) {
results = results.Where(s => s.StatementStatus == StatementStatusType.StatusB);
}
else {
// do nothing
}
}
results = .Take(100)
.Select(s => new StatementSearchResultDTO{ ....
};
// .. now you can you results.
}
Yeah, totally ugly, but now your database solves how to find Statements that satisfy the filter. Therefore, this request is quickly as possible. Now we must understand what magic happens in code I written upper. Let's compare two examples of code:
results = results.Where(s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber));
And this:
Func<Statement, bool> filter = s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber);
results = results.Where(filter);
What the difference? Why first is more faster? Answer: when compiler sees first code, it examines that type of results is IQueryable<T> and IEnumerable<T> so that condition inside of brackets can have type Func<Statement, bool> (compiled function) or Expression<Func<Statement, bool>> (data, that can be compiled in function). And compiler chooses Expression (why - really dunno, just chooses). After request of first object query compiled not in C# statement, but in SQL statement and sends to server. Your SQL server can optimize request, because of indexes existing.
Well, the more better way - to write your own expressions. There are different ways to write your own expression, but there is a way to write it with not ugly syntax. The problem that you can't just invoke one expression from another - that doesn't supported by Entity Framework and can be not supported by another ORM's. So, we can use PredicateBuilder by Pete Montgomery: link. And then write two simple extensions on expressions suitable for us.
public static Expression<Func<T, bool>> OrNullable<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
if (first != null && second != null)
return first.Compose(second, Expression.OrElse);
if (first != null)
return second;
if (second != null)
}
And that same for And. And now we can write our filter:
{
Expression<Func<Statement, bool>> filterByStatusA = null;
Expression<Func<Statement, bool>> filterByStatusB = null;
if (request.StatusA)
filterByStatusA = s => s.StatementStatus == StatementStatusType.StatusA;
if (request.StatusB)
filterByStatusB = s => s.StatementStatus == StatementStatusType.StatusB;
Expression<Func<Statement, bool>> filterByStatuses = filterByStatusA.OrNullable(filterByStatusB);
Expression<Func<Statement, bool>> filterByItemNumber = null;
if (!string.IsNullOrEmpty(request.ItemNumber))
filterByItemNumber = s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber);
Expression<Func<Statement, bool>> endFilter = filterByItemNumber.And(filterByStatuses);
requests = ...;
if (endFilter != null)
requests = requests.Where(endFilter);
}
You can got a problem, because class ExpressionVisitor in PredicateBuilder in .NET < 4.0 is sealed. You can get write your own ExpressionVisitor or just copy it from this article.
OK, here is the way I have solved it:
filter.And(s => (request.StatusA && s.StatementStatus == StatementStatusType.StatusA) ||
(request.StatusB && s.StatementStatus == StatementStatusType.StautsB) ||
!(request.StatusA || request.StatusB)); //None selected = All selected
Any comments?

linq function to handle : both fields equal or both null or empty

Hi I have a linq query where I compare an object with all other entity stored in my database. Firstname and lastname are mandatory fields so I don't have to check for null. But in the case of street I have to do these checks.
I want to match if both fields are null or empty strings or that they are the same. The below linq query is working fine.
But I was wandering isn't there any way to make it more readable. For example with a custom function like Bool FieldsAreEqualOrBothNullOrEmpty(r.street, request.street)
Can't figure how to do that and if it's possible.
var same =
from r in db.Requests
where r.firstName.ToLower() == request.firstName.ToLower()
&& r.lastName.ToLower() == request.lastName.ToLower()
//Seems long to just compare two fields
&& ( string.IsNullOrEmpty(r.street) && string.IsNullOrEmpty(request.street) )
|| r.street.ToLower() == request.street.ToLower()
select r;
return same;
I would simplify:
var same =
from r in db.Requests
where r.firstName.ToLower() == request.firstName.ToLower()
&& r.lastName.ToLower() == request.lastName.ToLower()
select r;
if(string.IsNullOrEmpty(request.street)) {
same = same.Where(r => string.IsNullOrEmpty(r.street));
} else {
string street = request.street.ToLower();
same = same.Where(r => r.street.ToLower() == street);
}
The nice thing about the this is that it keeps the query simple in each case.
You could use similar logic for the first two if you choose, and it could also be moved to an expression-based utility method. Untested:
public static IQueryable<T> FieldsAreEqualOrBothNullOrEmpty<T>(
this IQueryable<T> source,
Expression<Func<T, string>> member, string value)
{
Expression body;
if (string.IsNullOrEmpty(value))
{
body = Expression.Call(
typeof(string), "IsNullOrEmpty", null, member.Body);
}
else
{
body = Expression.Equal(
Expression.Call(member.Body, "ToLower", null),
Expression.Constant(value.ToLower(), typeof(string)));
}
return source.Where(Expression.Lambda<Func<T,bool>>(
body, member.Parameters));
}
then:
var same = db.Requests
.FieldsAreEqualOrBothNullOrEmpty(x => x.firstName, request.firstName)
.FieldsAreEqualOrBothNullOrEmpty(x => x.lastName, request.lastName)
.FieldsAreEqualOrBothNullOrEmpty(x => x.street, request.street);
You can just call String.Equals but ignore case, using an appropriate StringComparison.
string.equals(r.street, request.street, StringComparison.OrdinalIgnoreCase);
It's bad practice to convert strings to lower-case for comparison purposes, as it won't reliably work in all languages, and is slower than using a comparison which ignores case. See, e.g., this answer
How about creating extension method on string IsEqualOrBothNullOrEmpty -
public static class Extensions
{
public static bool IsEqualOrBothNullOrEmpty(this string firstValue,
string secondValue)
{
return string.IsNullOrEmpty(firstValue) &&
string.IsNullOrEmpty(secondValue)
|| firstValue.ToLower() == secondValue.ToLower();
}
}
And use in your query like this -
var same =
from r in db.Requests
where r.firstName.ToLower() == request.firstName.ToLower()
&& r.lastName.ToLower() == request.lastName.ToLower()
&& r.street.IsEqualOrBothNullOrEmpty(request.Street)
select r;
return same;

Linq to Entity - How to concatenate conditions

I am writing a Linq Query. Is there a way that I can concatenate to query based on some if conditions?
Like on query is
from res in _db.Person
where res.Departments.ID == deptId
select res;
And if I have a condition true, I would like it to be something like
from res in _db.Person
where res.Departments.ID == deptId && res.Departments.Type == deptType
select res;
Implementing an "AND" type condition is easy - and easier using extension method syntax to call Where multiple times:
IQueryable<Person> people = _db.Person
.Where(res => res.Departments.ID == deptId);
if (deptType != null)
{
people = people.Where(res => res.Departments.Type == deptType);
}
// Potentially add projections etc.
EDIT: If you want "OR" functionality, it's slightly tricky from scratch, as you need to mess around with expression trees. I suggest you use the PredicateBuilder library:
Expression<Func<Person, bool> predicate = res => res.Departments.ID == deptId;
if (deptType != null)
{
predicate = predicate.Or(res => res.Departments.Type == deptType);
}
IQueryable<Person> people = _db.Person.Where(predicate);
Assuming your condition is in the variable condition
from res in _db.Person
where res.Departments.ID == deptId && (!condition || res.Departments.Type == deptType)
select res;
Version that does or as requested
from res in _db.Person
where res.Departments.ID == deptId || (condition && res.Departments.Type == deptType))
select res;
Alternatively you may wish to use predicate builder
I would do something like this:
var result = _db.Person.Where(x=>x.Departments.ID == deptId);
if(myCondition)
result = result.Where(x=>x.Departments.Type == deptType);
The query is not actually executed until you attempt to enumerate result, so you can keep adding conditions as long as you like.

Dynamic where condition in LINQ

I have a scenario where I have to use a dynamic where condition in LINQ.
I want something like this:
public void test(bool flag)
{
from e in employee
where e.Field<string>("EmployeeName") == "Jhom"
If (flag == true)
{
e.Field<string>("EmployeeDepartment") == "IT"
}
select e.Field<string>("EmployeeID")
}
I know we can't use the 'If' in the middle of the Linq query but what is the solution for this?
Please help...
Please check out the full blog post: Dynamic query with Linq
There are two options you can use:
Dynamic LINQ library
string condition = string.Empty;
if (!string.IsNullOrEmpty(txtName.Text))
condition = string.Format("Name.StartsWith(\"{0}\")", txtName.Text);
EmployeeDataContext edb = new EmployeeDataContext();
if(condition != string.empty)
{
var emp = edb.Employees.Where(condition);
///do the task you wnat
}
else
{
//do the task you want
}
Predicate Builder
Predicate builder works similar to Dynamic LINQ library but it is type safe:
var predicate = PredicateBuilder.True<Employee>();
if(!string.IsNullOrEmpty(txtAddress.Text))
predicate = predicate.And(e1 => e1.Address.Contains(txtAddress.Text));
EmployeeDataContext edb= new EmployeeDataContext();
var emp = edb.Employees.Where(predicate);
difference between above library:
PredicateBuilder allows to build typesafe dynamic queries.
Dynamic LINQ library allows to build queries with dynamic Where and OrderBy clauses specified using strings.
So, if flag is false you need all Jhoms, and if flag is true you need only the Jhoms in the IT department
This condition
!flag || (e.Field<string>("EmployeeDepartment") == "IT"
satisfies that criterion (it's always true if flag is false, etc..), so the query will become:
from e in employee
where e.Field<string>("EmployeeName") == "Jhom"
&& (!flag || (e.Field<string>("EmployeeDepartment") == "IT")
select e.Field<string>("EmployeeID")
also, this e.Field<string>("EmployeeID") business, smells like softcoding, might take a look into that. I guess
from e in employee
where e.EmployeeName == "Jhom"
&& (!flag || (e.EmployeeDepartment == "IT")
select e.EmployeeID
would be more compact and less prone to typing errors.
EDIT: This answer works for this particular scenario. If you have lots of this kinds of queries, by all means investingate the patterns proposed in the other answers.
You can chain methods :
public void test(bool flag)
{
var res = employee.Where( x => x.EmployeeName = "Jhom" );
if (flag)
{
res = res.Where( x => x.EmployeeDepartment == "IT")
}
var id = res.Select(x => x.EmployeeID );
}
from e in employee
where e.Field<string>("EmployeeName") == "Jhom" &&
(!flag || e.Field<string>("EmployeeDepartment") == "IT")
select e.Field<string>("EmployeeID")
You can call LINQ methods explicitly and chain them conditionally.
public IEnumerable<string> FilterEmployees (IEnumerable<Employee> source, bool restrictDepartment)
{
var query = source.Where (e => e.Field<string>("EmployeeName") == "Jhom");
if (restrictDepartment) // btw, there's no need for "== true"
query = query.Where (e => e.Field<string>("EmployeeDepartment") == "IT");
return query.Select (e => e.Field<string>("EmployeeID"));
}

Categories

Resources