1

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?

Lamloumi Afif
  • 8,941
  • 26
  • 98
  • 191
  • Don't use reflection,even less with linq-to-entities which doesn't support it. Why don't you have a method `GetAllUsers` and one `GetAllFoo` and so on`? – Tim Schmelter Mar 11 '16 at 15:57

2 Answers2

9

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).

Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
3

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();
    }
}
CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • thank you. I can't implement the interface `IDeletable` interface for all the `T` because the `T` class are auto-generated(from edmx) – Lamloumi Afif Mar 11 '16 at 16:04
  • You can, because they are [generated as partial classes](http://stackoverflow.com/questions/5044596/making-entity-framework-implement-an-interface). – CodeCaster Mar 11 '16 at 16:04
  • But I have to edit it in every re-generation of the model !! – Lamloumi Afif Mar 11 '16 at 16:06
  • No, you don't, they are partial classes. Create a new file, using the same namespace as your EDMX entities, in which you create partial classes like `public partial class SomeEntity : IDeletable { }` and you're good to go. Try searching, see for example [Generating Interfaces from entity framework database first auto-generated code](http://stackoverflow.com/questions/12348734/generating-interfaces-from-entity-framework-database-first-auto-generated-code). – CodeCaster Mar 11 '16 at 16:06
  • Yes I know but assume I add couples of entities to the model so I have to add the implementation of the interface for the new class everytime – Lamloumi Afif Mar 11 '16 at 16:12
  • Yes, every time you add a new entity which you want to use with this generic repository or whatever it is, you'll have to create a matching second partial class and apply the interface to that entity. – CodeCaster Mar 11 '16 at 16:13