How to Dynamically Create an Expression<Func<Myclass, Bool>> Predicate from Expression<Func<Myclass, String>>

How do I dynamically create an ExpressionFuncMyClass, bool predicate from ExpressionFuncMyClass, string?

It's hard to mix compiler-generated expression trees and hand-made ones, precisely because of this sort of thing - extracting out the ParameterExpressions is tricky. So let's start from scratch:

ParameterExpression argParam = Expression.Parameter(typeof(Service), "s");
Expression nameProperty = Expression.Property(argParam, "Name");
Expression namespaceProperty = Expression.Property(argParam, "Namespace");

var val1 = Expression.Constant("Modules");
var val2 = Expression.Constant("Namespace");

Expression e1 = Expression.Equal(nameProperty, val1);
Expression e2 = Expression.Equal(namespaceProperty, val2);
var andExp = Expression.AndAlso(e1, e2);

var lambda = Expression.Lambda<Func<Service, bool>>(andExp, argParam);

One important aspect I've changed is the type passed to Expression.Parameter - it certainly looks like it should be a Service rather than a string.

I've given that a try, and it seemed to work when I called lambda.Compile and executed it on a couple of sample Service objects...

How do I dynamically create an ExpressionFuncMyClass, bool predicate?

Original

Like so:

    var param = Expression.Parameter(typeof(string), "p");
var len = Expression.PropertyOrField(param, "Length");
var body = Expression.Equal(
len, Expression.Constant(5));

var lambda = Expression.Lambda<Func<string, bool>>(
body, param);

Updated

re (p.Length== 5) && (p.SomeOtherProperty == "hello"):

var param = Expression.Parameter(typeof(SomeType), "p");
var body = Expression.AndAlso(
Expression.Equal(
Expression.PropertyOrField(param, "Length"),
Expression.Constant(5)
),
Expression.Equal(
Expression.PropertyOrField(param, "SomeOtherProperty"),
Expression.Constant("hello")
));
var lambda = Expression.Lambda<Func<SomeType, bool>>(body, param);

Dynamically constructing an ExpressionFuncT,bool doesn't work for GreaterThen

The problem is the nullability. You could probably get away with just adding a conversion expression from the value to the type of the property:

public Expression<Func<Book, bool>> GreaterThan(string columnName, object value)
{
var param = Expression.Parameter(typeof(Book), "w");
var property = Expression.PropertyOrField(param, columnName);
var propertyType = typeof(Book).GetProperty(columnName).PropertyType;
var body = Expression.GreaterThan(property,
Expression.Convert(Expression.Constant(value), propertyType));

return Expression.Lambda<Func<Book, bool>>(body, param);
}

This is now effectively doing:

w => w.PublishedYear > (int?) 2005

... which is what the C# code is doing implicitly.

Create expression tree (ExpressionFuncTEntity, bool) with property of entity (x.ID == 123)

Based on your description / comments, the following should work for you:

public Expression<Func<TEntity, bool>> SearchExpression()
{
ConstantExpression[] expectedValues = Your_Magic_Method_Of_Obtaining_Expected_Values();

var entity = Expression.Parameter(typeof (TEntity));

var comparisonExpression = typeof(TEntity).GetProperties()
.Select((info, i) => Expression.Equal(
Expression.Property(entity, info),
expectedValues[i]))
.Aggregate(Expression.And);
return Expression.Lambda<Func<TEntity, bool>>(comparisonExpression, entity);
}

Composing invocations with ExpressionFuncT,bool the same way as FuncT,bool

Unfortunately, C# does not currently provide a way to compose expressions from Expression<Func<...>> objects. You have to use expression trees, which is quite a bit longer:

static Expression<Func<T,bool>> CheckExpr<T>(Expression<Func<T,Customer>> conv, string first, string last) {
var arg = Expression.Parameter(typeof(T));
var get = Expression.Invoke(conv, arg);
return Expression.Lambda<Func<T,bool>>(
Expression.MakeBinary(
ExpressionType.AndAlso
, Expression.MakeBinary(
ExpressionType.Equal
, Expression.Property(get, nameof(Customer.FirstName))
, Expression.Constant(first)
)
, Expression.MakeBinary(
ExpressionType.Equal
, Expression.Property(get, nameof(Customer.LastName))
, Expression.Constant(last)
)
)
, arg
);
}

Extending an expressionfunct, bool

You have to build new expression, because expressions are immutable. In this case you can reuse body and parameters of existing expression for the new one:

public class TenantFilterExpressionAdaptor<T, TIdType> {

public TIdType TenantId { get; set; }

public Expression<Func<T, bool>> Adapt(Expression<Func<T, TIdType>> idExpression) {
return Expression.Lambda<Func<T, bool>>(
Expression.Equal(idExpression.Body, Expression.Constant(TenantId)),
idExpression.Parameters);
}
}

You mentioned in comments that constant is problematic for you. In this case you can reference property itself and not its value:

return Expression.Lambda<Func<T, bool>>(
Expression.Equal(idExpression.Body, Expression.Property(Expression.Constant(this), nameof(TenantId))),
idExpression.Parameters);

This will be expression like:

x => x.TenantId == this.TenantId

where this is instance of your adapter.

Unable to create a compound ExpressionFuncstring, bool from a set of expressions

If you wanted to do it with Expressions, something like this would work. This doesn't short circuit, though you can build that in. You were quite close. You need to thread one parameter expression through the whole parameter tree.

string badValue = "hello!";
const int minSize = 8;
const int maxSize = 30;

Expression<Func<string, bool>> stringLengthMax = value => value.Length < maxSize;
Expression<Func<string, bool>> stringLengthMin = value => value.Length > minSize;
Expression<Func<string, bool>> isEmpty = value => !string.IsNullOrEmpty(value);

ParameterExpression pe = Expression.Parameter(typeof(string));

var x = Expression.Lambda<Func<string, bool>>(
Expression.And(Expression.Invoke(stringLengthMax, pe),
Expression.And(Expression.Invoke(stringLengthMin, pe), Expression.Invoke(isEmpty, pe))), pe);

Func<string, bool> shouldValidate = x.Compile();
bool resultFalse1 = shouldValidate("hello!");
bool resultFalse2 = shouldValidate("1234567890123456789012345678901");
//bool resultFalse3 = shouldValidate(null); Throws an exception because you can't do (null).Length
bool shouldBeTrue = shouldValidate("123456789");

//LinqPad code to view results:
resultFalse1.Dump();
resultFalse2.Dump();
//resultFalse3.Dump();
shouldBeTrue.Dump();


Related Topics



Leave a reply



Submit