Lamloumi Afif
Lamloumi Afif

Reputation: 9081

Use reflection and lambda expression with linq

I need to filter a collection of items by verifing the value of a flag named deletion_date

 public List<T> GetAll()
 {
   if (context == null) context = new ajtdevEntities();
   return context.Set<T>().Where(p => p.GetType().GetProperty("deletion_date") == null).ToList();
 }

I get an exception when I used this generic method

LINQ to Entities does not recognize the method ' System.Reflection.PropertyInfo GetProperty ( System.String )' , and the latter can not be translated into term store.

How can I fix this method?

Upvotes: 2

Views: 3438

Answers (2)

Ivan Stoev
Ivan Stoev

Reputation: 205539

Instead of reflection, you can build the filter expression manually using System.Linq.Expressions like this:

public List<T> GetAll<T>()
{
    var parameter = Expression.Parameter(typeof(T), "p");
    var predicate = Expression.Lambda<Func<T, bool>>(
        Expression.Equal(Expression.PropertyOrField(parameter, "deletion_date"), Expression.Constant(null)),
        parameter);
    if (context == null) context = new ajtdevEntities();
    return context.Set<T>().Where(predicate).ToList();
}

Note that the above will throw exception if your type does not have property/field called "deletion_date" or the type of the property does not support null. But the same can be said for your reflection based implementation (if it worked).

Upvotes: 10

CodeCaster
CodeCaster

Reputation: 151588

An ORM will inspect the lambda and convert its parts to SQL. The Entity Framework team chose not to support reflection, and rightfully so. So it can't translate GetProperty() calls to SQL, hence the error you get.

It wouldn't work anyway, because GetProperty() gets a PropertyInfo instance, not the value. So if it were null, that would indicate that the type of p has no property named deletion_date.

The proper way would be to call GetValue() on the PropertyInfo (note that this will throw a NullReferenceException if there is no property named thusly):

p => p.GetType().GetProperty("deletion_date").GetValue(p)

But again, reflection is not supported in Entity Framework, so you want to use interfaces:

public interface IDeletable
{
    DateTime? deletion_date { get; set; }
}

And apply that to your class or method as a generic constraint. Then you can use it in your lambda:

public class WhateverClass<T>
    where T : IDeletable
{
    public List<T> GetAll()
    {
        return context.Set<T>().Where(p => p.deletion_date == null).ToList();
    }
}

Upvotes: 3

Related Questions