Conditional LINQ clauses

Programming LINQ C#

The Language Integrated Query (LINQ) syntax is arguably one of the most powerful features of .NET. The ability to express queries over different data sources declaratively has made the .NET ecosystem more productive.

This article explain how to use the if condition to add additional Where constraints based on a condition.

Because Where is producing an IQueryable, the execution is deferred until the ToList method is called, so you can chain Where constraints together as much as you want and then just execute it after you have passed all your conditions (we'll see that later).

For example lets look at this piece of code:

// original query
var query = someList.Where(q => q == "something");

// add additional where constraints when condition X has been met
if (condition)
{
query = query.Where(x => x == "something else");
}

var result = query.ToList();

Instead of

if (condition)
{
query = query.Where(x => x == "something else");
}

we could create an extension method (here: named If) that would make this piece of code much more smoother:

query = query.If(condition, x => x == "something else");

By combining several conditional LINQ clauses we can implement a very straight forward "conditional filter function" like:

//search for this&that Y/N
bool searchForFirstName = false;
bool searchForLastName = false;
bool searchForOnlyFemale = true;
bool searchForYearOfBirthGreaterThan = true;
bool searchForSocialSecurityGreaterThan = true;

// search"terms"
string searchTermFirstName = "Fox";
string searchTermLastName = "Mulder";
int searchTermYearOfBirth = 1980;
int searchTermSocialSecNr = 420000;

theData = PopulateData();
theData = theData.If(searchForFirstName,
q => q.Where(_ => _.FirstName == searchTermFirstName))
.If(searchForLastName,
q => q.Where(_ => _.LastName == searchTermLastName))
.If(searchForOnlyFemale,
q => q.Where(_ => _.SexIsFemale))
.If(searchForYearOfBirthGreaterThan,
q => q.Where(_ => _.DayOfBirth.Year > searchTermYearOfBirth))
.If(searchForSocialSecurityGreaterThan,
q => q.Where(_ => _.SocialSecurityNr > searchTermSocialSecNr))
.ToList();

So here's the full example source code:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleAppConditionalLinq
{
public static class LINQExtensions
{
public static IQueryable<T> If<T>(
this IQueryable<T> query,
bool should,
params Func<IQueryable<T>, IQueryable<T>>[] transforms)
{
return should
? transforms.Aggregate(query,
(current, transform) => transform.Invoke(current))
: query;
}

public static IEnumerable<T> If<T>(
this IEnumerable<T> query,
bool should,
params Func<IEnumerable<T>, IEnumerable<T>>[] transforms)
{
return should
? transforms.Aggregate(query,
(current, transform) => transform.Invoke(current))
: query;
}
}

class DataModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool SexIsFemale { get; set; }
public DateTime DayOfBirth { get; set; }
public int SocialSecurityNr { get; set; }
}

class Program
{
// data to be searched
static List<DataModel> theData;

// search for this&that Y/N
static bool searchForFirstName = false;
static bool searchForLastName = false;
static bool searchForOnlyFemale = true;
static bool searchForYearOfBirthGreaterThan = true;
static bool searchForSocialSecurityGreaterThan = true;

// search"terms"
static string searchTermFirstName = "Fox";
static string searchTermLastName = "Mulder";
static int searchTermYearOfBirth = 1980;
static int searchTermSocialSecNr = 420000;

static void Main(string[] args)
{
PopulateAndFilterData();
DisplayData(theData);

Console.WriteLine("");
Console.WriteLine("now search with searchForOnlyFemale set to FALSE:");
Console.WriteLine("");

// now don't only search exclusive for women
searchForOnlyFemale = false;
PopulateAndFilterData();
DisplayData(theData);

Console.WriteLine("");
Console.WriteLine("now search for LastName='Smith'");
Console.WriteLine("");

// now only search for lastname like('Smith') and soc.sec.nr > 300000
searchForFirstName = false;
searchForLastName = true;
searchForOnlyFemale = false;
searchForYearOfBirthGreaterThan = false;
searchForSocialSecurityGreaterThan = true;

searchTermLastName = "Smith";
searchTermSocialSecNr = 300000;

PopulateAndFilterData();
DisplayData(theData);

Console.ReadKey();
}

static void PopulateAndFilterData()
{
theData = PopulateData();
theData = theData.If(searchForFirstName,
q => q.Where(_ => _.FirstName == searchTermFirstName))
.If(searchForLastName,
q => q.Where(_ => _.LastName == searchTermLastName))
.If(searchForOnlyFemale,
q => q.Where(_ => _.SexIsFemale))
.If(searchForYearOfBirthGreaterThan,
q => q.Where(_ => _.DayOfBirth.Year > searchTermYearOfBirth))
.If(searchForSocialSecurityGreaterThan,
q => q.Where(_ => _.SocialSecurityNr > searchTermSocialSecNr))
.ToList();
}

static List<DataModel> PopulateData()
{
List<DataModel> res = new List<DataModel>
{
new DataModel
{
Id = 1,
FirstName = "John",
LastName = "Smith",
SexIsFemale = false,
DayOfBirth = new DateTime(1972, 8, 25),
SocialSecurityNr = 338921
},
new DataModel
{
Id = 2,
FirstName = "Sandy",
LastName = "Holmes",
SexIsFemale = true,
DayOfBirth = new DateTime(1984, 3, 12),
SocialSecurityNr = 426187
},
new DataModel
{
Id = 3,
FirstName = "Jack",
LastName = "Brown",
SexIsFemale = false,
DayOfBirth = new DateTime(1988, 12, 1),
SocialSecurityNr = 486187
},
new DataModel
{
Id = 4,
FirstName = "Evelyn",
LastName = "Johnson",
SexIsFemale = true,
DayOfBirth = new DateTime(1979, 4, 30),
SocialSecurityNr = 399461
},
new DataModel
{
Id = 5,
FirstName = "Harry",
LastName = "Davis",
SexIsFemale = false,
DayOfBirth = new DateTime(1992, 6, 13),
SocialSecurityNr = 506274
}
};
return res;
}

static void DisplayData(List<DataModel> data)
{
foreach (var d in data)
{
Console.Write($"{d.FirstName} {d.LastName}");
Console.Write($" born in {d.DayOfBirth.Year}");
Console.Write($" with SocSecNr {d.SocialSecurityNr}");
Console.WriteLine($" (is female: {d.SexIsFemale})");
}
}
}
}