HiveBrain v1.2.0
Get Started
← Back to all entries
snippetcsharpMinor

Generate a LINQ Expression Tree from a filter string

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
expressionlinqgeneratefilterfromstringtree

Problem

I have a C# Service that takes a filter string and a FilterMap class and generates an Expression> for use within Linq to * Scenarios (currently it is in use to create dynamic filtering for documentdb queries).

Filters have the form parameter_operation_value, for example: firstName_eq_Joe. Multiple filters can be contained within the same string if they are space delimited, for example: firstName_eq_Joe lastName_eq_Bloggs.

The FilterMap class is a simple POCO that states what filters are available and where they exist on the target object. Each property on the FilterMap is a string, where the property name is the property part of a filter and its value is where that property lives on T.

Example FilterMap class:

public class FilterMap
{
    public string Reputation => "Reputation";
    public string FirstName => "User.FirstName";
    public string LastName => "User.LastName";
}


In the example above Reputation is mapped to the property Reputation. FirstName is mapped to an object property on T called User and then a property on that User object called FirstName.

The code for the service can be found below:

```
public class FilterService : IFilterService where TModel : class
{
private readonly FilterTypeFactory _filterTypeFactory;
private readonly Lazy> _modelPropertiesLazy;

public FilterService()
{
_filterTypeFactory = new FilterTypeFactory();
_modelPropertiesLazy = new Lazy>(() => typeof(TModel).GetTypeInfo().GetProperties(BindingFlags.Instance | BindingFlags.Public));
}

public Expression> GenerateFilterFromFilterMapSync(string filterString) where TFilterMap : class, new()
{
IEnumerable filters = ParseFilterString(filterString);

if (!filters.Any())
{
return null;
}

IEnumerable filterMapProperties = typeof(TFilterMap).GetTypeInfo().GetProperties(BindingFlags.Instance | BindingFlags.Public);

TFilterMap filterMa

Solution

internal abstract class BaseFilterType

The GetPropertyExpression() just got into my eyes asking myself what all of the case evaluations means. The same will likely happen to Sam the maintainer while hunting down a bug or adding functionality to that class.

Instead of having values in the case statements like "eq", "ne", "ct" etc you should either extract this to meaningful constants like e.g

private const string equal = eq";
private const string notEqual = "ne";


or use an enum like so

public enum Operation
{
    Equal, NotEqual, GreaterThan, and so on
}


Using an enum approach would need to change your FilterInfo class to use this Operation enum instead of string but would come with the benefit that you wouldn't need to call ToLowerInvariant().

You could replace the internal readonly IEnumerable _allowedOperations; with Operation as well by using the [FlagsAttribute] on the enum so you can combine the enums by AND, OR, EXCLUSIVE OR. Using the FalgsAttribute should go hand in hand with naming the enum using a plural form, hence Operations should then be used.

internal class FilterTypeFactory

I would extract the case TypeCode.Object: in the GetFilterType() method to a separate method because that method/case is too big.

In addition instead of accessing the PropertyType of the PropertyInfo over and over again, I would access it once, store it in a variable and check against this variable.

Code Snippets

private const string equal = eq";
private const string notEqual = "ne";
public enum Operation
{
    Equal, NotEqual, GreaterThan, and so on
}

Context

StackExchange Code Review Q#156208, answer score: 4

Revisions (0)

No revisions yet.