All in one searching for dummies & advanced users with Expression Trees, Dynamic Linq and Funcy Linq Magic#

Currently having fun with #DynamicLinq #LambdaParsing #ExpressionTrees and more… – I was blown away by the power of Expressions and Dynamic Linq (parsing) – thus I wanted to share this code & ideas already. It’s far from finished and still needs to be fleshed out, to make it user friendly (frontend) – but at least the ideas, capabilities and tech for the backend are already present to make a flexible solution that caters both to users that want a quick search, and users that will want a (typed?) refined searchall in one searchbox.

17022049_10155053097151091_4004970426825369471_n

Why again? I’m building a rich query back end for both “dummy match all searches” as well as really specialized queries for when people are a bit more advanced and want to tap into some sql-like query strings in the inputbox.. So, yes, it needs to support one inputbox, the parser will figure out which #searchstrategy to use..

I was having loads of fun with #ExpressionTrees #Func and #DynamicLinq #DotNet #Programming #CSharp and I wanted to share some of the code & NuGet packages I used for this solution so far. All suggestions & critique are welcome.

1. Supporting Linq to Entities searches with Expressions…

It might sound easier than it is, but Linq to Entities (Linq with Entity Framework) doesn’t translate everything to SQL as you’d think… To support this I found the LinqKit project, which gives me the power to use Expression Trees inside any Linq to Entities query. Mostly I’m using a predicate based model as Func<Entity, bool>, but here’s my actual

actual implementation as reference:

// other using statements
using LinqKit;

// Search method accepting Expressions of Func<Person, bool> that can be evaluated
// read more about "AsExpandable" at LinqKit: https://github.com/scottksmith95/LINQKit
public IEnumerable<Person> Search(params Expression<Func<Person, bool>>[] predicates{
    var query = _dataContext.People.AsExpandable();
 
    foreach (var predicate in predicates) {
        query = query.Where(person => predicate.Invoke(person));
    }

    return query.AsEnumerable();
}

2. Support Text-Based Expression Trees to actual Expression Trees in code

The next step was to support a text-based version of such an Expression, for example, if a user wants to search on a person’s “FirstName” and only those that contain “oen” inside the “FirstName”. Then we’d end up with a text-based expression that looks a bit like this: “FirstName.Contains(“oen”)”; or if we’d want the “FirstName” to start with “Lil” we’d end up with code that looks a bit like this: “FirstName.StartsWith(“Lil”)“.

To convert those text-based C# like code / Expressions I used the System.Linq.Dynamic project – I’ll let the code below speak for itself (it uses the above example to execute the Expression it creates), but be sure to visit https://github.com/kahanu/System.Linq.Dynamic for more information on the project.

public IEnumerable<Person> Search(string query{
    // parsing the "query", 
    //   e.g.: "(FirstName.Trim() + LastName.Trim()).ToLower().Contains(\"lil\".Trim().ToLower())"
    var expression = System.Linq.Dynamic.DynamicExpression.ParseLambda<Person, bool>(query);
    // passing the parsed expression to the Search method (see the code in point 1)
    return Search(expression);
}

Here are some of the queries that are possible using this approach…

// Using the base search method in point 1
var personId = 33311;
var searchResults = personRepository.Search(
    p => p.Id == personId
);
Console.WriteLine($"Searching on personId '{personId}':");
foreach (var person in searchResults) {
    Console.WriteLine($" - {person}");
}

// Search method with an text-based expression
var expression = "(FirstName + LastName).Contains(\"oen\")";
searchResults = personRepository.Search(
    expression
);
Console.WriteLine($"Searching with expression '{expression}':");
foreach (var person in searchResults) {
    Console.WriteLine($" - {person}");
}

// Idea for the base "dummy" Search method strategy
// uses dynamic linq query syntax, but we'll need to differentiate it inside the api...
var searchInput = "Annelies";
expression = $"(FirstName.Trim() + LastName.Trim() + Id.ToString().Trim()).ToLower().Contains(\"{searchInput}\".Trim().ToLower())";
searchResults = personRepository.Search(
    expression
);
Console.WriteLine($"Searching with expression '{expression}':");
foreach (var person in searchResults) {
    Console.WriteLine($" - {person}");
}

3. Strategy & approach… it’s just a test / POC at the moment!

I was blown away by the power of Expressions and Dynamic Linq (parsing) – thus I wanted to share this code & ideas already. It’s far from finished and still needs to be fleshed out, to make it user friendly (frontend) – but at least the ideas, capabilities and tech for the backend are already present to make a flexible solution that caters both to users that want a quick search, and users that will want a (typed?) refined searchall in one searchbox.

17022049_10155053097151091_4004970426825369471_n