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.
My personal experience with LINQ has traversed over many different LINQ providers from LINQ2SQL, Entity Framework, LINQ to Objects, and so many more. That is why I find the following extension methods so useful, regardless of the specific LINQ Provider.
There are times we want to apply an expression only when the system provides a reason to do so. A typical example is filtering our dataset based on the current user’s role within our system. We may also want to apply a different transformation based on a system configuration setting.
For these use cases, I’ve written an extension method that applies to IQueryable
and IEnumerable
interfaces. Targeting these two interfaces gives us the most coverage across most LINQ provider implementations.
// don't forget to reference these namespaces
// using System.Collections.Generic;
// using System.Linq;
public static class EnumerableHelpers
{
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;
}
}
The extension methods will only apply the query transformations when the should
value is true
. The boolean can originate from any source within our application.
Here is an example that filters an array of values based on whether a user is a “regular” user or a “super” user.
class Program
{
static void Main(string[] args)
{
var data = new[]
{
new {user = "captain", data = "launch codes"},
new {user = "doctor", data = "medicine"},
new {user = "lawyer", data = "law"}
};
var administratorFlags = new [] { true, false };
var user = "captain";
foreach (var regularUser in administratorFlags)
{
var results =
data
.If(regularUser,
q => q.Where(x => x.user == user)
)
.Select(x => x.data)
.ToList();
Console.WriteLine(new string('.', 50));
Console.WriteLine($"I am a {(regularUser ? "regular" : "SUPER")} {user}");
Console.WriteLine($"My data includes {string.Join(", ", results)}.");
}
Console.WriteLine(new string('.', 50));
}
}
Running the program gets us the filtered and unfiltered results, respectively.
..................................................
I am a regular captain
My data includes launch codes.
..................................................
I am a SUPER captain
My data includes launch codes, medicine, law.
..................................................
The extension methods are potent additions to anyone’s LINQ toolbox. If you are using these extension methods, please leave a comment below on how and why you find them helpful.