Contains .NET Expression Helpers
Package Manager:
PM> Install-Package Spinit.Expressions
.NET CLI:
> dotnet add package Spinit.Expressions
Combines two predicates using AndAlso (&&) and uses parameters from the first expression.
Example:
Expression<Func<int, bool>> first = x => x > 10;
Expression<Func<int, bool>> second = y => y < 20;
var result = first.And(second); // combines the expressions using AndAlso
// result expression: (int x) => x > 10 && x < 20
Combines two predicates using OrElse (||) and uses parameters from the first expression.
Example:
Expression<Func<int, bool>> first = x => x >= 10;
Expression<Func<int, bool>> second = y => y <= -10;
var result = first.Or(second); // combines the expressions using OrElse
// result expression: (int x) => x >= 10 || x <= -10
Combines multiple predicates using the supplied operator and uses parameters from the first expression.
Example:
var predicates = new List<Expression<Func<string, bool>>>
{
x => x.Contains("a"),
y => y.Contains("b"),
z => z.Contains("c")
};
var result = predicates.Combine(CombineOperator.And);
// result expression (string x) => x.Contains("a") && x.Contains("b") && x.Contains("c")
RemapTo
is used to remap an expression on a type to another type, given a selector that is of the original type.
Example:
public class MyClass
{
public string Id { get; set; }
}
...
Expression<Func<string, bool>> stringExpression = s => s == "123";
var myClassExpression = stringExpression.RemapTo((MyClass x) => x.Id); // provide a path
// myClassExpression: (MyClass x) => x.Id == "123"
This is a utility class for generating an expression on a class given another "filter" class.
Example scenario: You have a webapi using some ORM (NHibernate, EntityFramework, Spinit.CosmosDb) and want to allow api users to supply a filter for your data.
Metacode for this scenario:
public class MyEntity
{
public string Id { get; set; }
...
public MyStatusEnum Status { get; set; }
public string Category { get; set; }
...
}
public class MyEntityFilter
{
[TargetPropertyName(nameof(MyEntityFilter.Status))] // This attribute is optional but recommended
public MyStatusEnum? Status { get; set; }
[TargetPropertyName(nameof(MyEntityFilter.Category))]
public IEnumerable<string> Category { get; set; }
}
public MyApiController : BaseController
{
private readonly IQueryable<MyEntity> _entities;
// WebApi action method
public IEnumerable<MyEntity> Get(MyEntityFilter filter)
{
var predicate = new PredicateBuilder<MyEntityFilter, MyEntity>().Generate(filter);
// if filter.Status and filter.Category is set the resulting expression looks like:
// x => x.Status == filter.Status && filter.Category.Contains(x.Category)
return _entities.Where(predicate);
}
}
Out of the box the PredicateGenerator
handles "simple types", eg types that contains a single value. You can supply your own IPropertyPredicateHandler
and add it using PredicateGenerator.AddHandler
.
Conventions:
- If no
TargetPropertyName
attribute exists the property name of the filter class and the entity must match. - When using an enumerable on the filter class
Contains
is used, eg it should match any of the supplied values (OR). - If the filter value is null (or an empty enumerable) no predicate is applied for the current property.
Utility class for defining a predicate expression e.g. Expression<Func<SomeType, bool>>
Example:
// instead of
Expression<Func<string, bool>> predicate = s => s == "123";
// you can use
var predicate = Predicate.Of<string>(s => s == "123");
Utility class for expressions from types.
Returns an expression that points to the specified property.
Example for sorting a collection on a dynamic property:
public class MyEntity
{
public string Name { get; set; }
}
public MyApiController : BaseController
{
private readonly IQueryable<MyEntity> _entities;
// WebApi action method
public IEnumerable<MyEntity> Get(string orderBy)
{
var orderByExpression = TypeExpressions.GetPropertyExpression<MyEntity>(orderBy);
// if orderBy == "Name" orderByExpression is: x => x.Name
return _entities.OrderBy(orderByExpression);
}
}