Search code examples
c#linq.net-corelambda

Refer to a class's property with an Expression, then use that Expression in a LINQ query


I need to be able to refer to an object's property through an Expression, which I do in this way:

Expression<Func<MyFirstModel, object>> MyFirstModelIdProperty = x => x.Id;

Intellisense is allowing me to do this, but I don't know for sure that's the correct declaration.

Now, I'm trying to actually use the property reference in a LINQ expression

int id = 500;
myFirstModelDataSet.SingleOrDefault(x=> MyFirstModelIdProperty == id); //??

Obviously, this isn't even compiling but I'm not sure how to go about.

Eventually, I'll have to also support this case

Expression<Func<MyFirstModel, object>> MyFirstModelIdProperty = x => x.Id;
Expression<Func<MySecondModel, object>> MySecondModelIdProperty = x => x.TheId;

MySecondModel secondModel = new MySecondModel() { TheId = 55 };
myFirstModelDataSet.SingleOrDefault(x=> MyFirstModelIdProperty == MySecondModelIdProperty); //??

The end goal here is that I want people to be able to inform my project how to query a DbSet by "id" without having to specify the properties literally (through strings).

Any ideas ?


Solution

  • Assuming your are working with IQueryable(otherwise just compile expression) - the easiest option would be to use LINQKit:

    myFirstModelDataSet.AsExpandable().SingleOrDefault(x=> MyFirstModelIdProperty.Invoke(x) == (object)id);
    

    Otherwise you will need to perform some expression tree compilation yourself:

    int someId = 42;
    var equalExp = Expression.Equal(MyFirstModelIdProperty.Body, Expression.Convert(Expression.Constant(someId), typeof(object)));
    var expression = Expression.Lambda<Func<Person, bool>>(equalExp, MyFirstModelIdProperty.Parameters);
    myFirstModelDataSet.SingleOrDefault(expression);
    

    For the second one you will need to perform parameter replacement which can easily be done with ReplacingExpressionVisitor. Something along these lines:

    var secondOp = new ReplacingExpressionVisitor(MySecondModelIdProperty .Parameters, MyFirstModelIdProperty.Parameters)
            .Visit(MySecondModelIdProperty.Body);
    
    var equalExp = Expression.Equal(MyFirstModelIdProperty.Body, secondOp);