Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I do a Func<object[],Expression<Func<T,bool>>> dynamic?

Tags:

c#

.net

lambda

I am working on doing a .Find(objects[] keys) method that dynamically creates an lambda function to query a database.

Basically what I want is:

var mykeys = new Guid("37ec1659-b35e-46c9-a7fc-e9802644ca1a");
IQueryable<T> database ;
Func<object[],Expression<Func<T,bool>>> objectFinder = CreateKeyExpression(typeof(T));
var foundObject = database.FirstOrDefault(objectFinder(mykeys));

and

private LambdaExpression CreateKeyExpression(Type C1Type)
{

    ParameterExpression instanceParameter = Expression.Parameter(C1Type);
    ParameterExpression keyParameters = Expression.Parameter(typeof(object[]));

    PropertyInfo[] objectKeys = C1Type.GetKeyProperties().ToArray();
    var expr = Expression.Equal( Expression.TypeAs( Expression.Property(instanceParameter,objectKeys[0]),typeof(object)),
      Expression.ArrayIndex(keyParameters,Expression.Constant(0))); 


    for (int i = 1; i < objectKeys.Length; i++)
    {
        expr = Expression.AndAlso(expr, Expression.Equal(
            Expression.Property(instanceParameter,objectKeys[i]),
            Expression.ArrayIndex(keyParameters,Expression.Constant(i))
            )); 

    }

    var lmp= Expression.Lambda(expr, instanceParameter);

    return Expression.Lambda(lmp, keyParameters);
}

Any ideas of how I can achieve this? The above gives me a Func<object[],Func<T,bool>>, which makes the IQueryable to a IEnumerable, meaning it won't do it on the database end.

like image 251
Poul K. Sørensen Avatar asked Nov 01 '22 16:11

Poul K. Sørensen


1 Answers

You need to use the Expression.Constant method instead of the Expression.ArrayIndex, because you will not be able to pass to your expression array with the key values ​​using the FirstOrDefault method.

private static LambdaExpression CreateKeyExpression(Type C1Type, object[] parameters)
{
    var instanceParameter = Expression.Parameter(C1Type);
    PropertyInfo[] objectKeys = C1Type.GetKeyProperties().ToArray();

    var expr = Expression.Equal(Expression.Property(instanceParameter, objectKeys[0]),
        Expression.Constant(parameters[0], objectKeys[0].PropertyType));


    for (int i = 1; i < objectKeys.Length; i++)
    {
        expr = Expression.AndAlso(expr, Expression.Equal(
            Expression.Property(instanceParameter, objectKeys[i]),
            Expression.Constant(parameters[i], objectKeys[i].PropertyType)));
    }
    return Expression.Lambda(expr, instanceParameter);
}

var parameters = new object[] { Guid.NewGuid(), Guid.NewGuid() };
var lambdaExpression = CreateKeyExpression(typeof(TestClass), parameters);
var testClasses = new List<TestClass>() { new TestClass { Id = (Guid)parameters[0], Id1 = (Guid)parameters[1] } };
var testClass = testClasses.AsQueryable().FirstOrDefault((Expression<Func<TestClass, bool>>)lambdaExpression);
like image 50
Vyacheslav Volkov Avatar answered Nov 08 '22 03:11

Vyacheslav Volkov