Alan
Alan

Reputation: 461

LINQ to Entities does not recognize method inside an Expression

I ran into a conceptional problem using EntityFramework/"Linq to Entities".

Say, we have a table and we're doing some filtering-operations based on a column called "ValidTo" (Type: DateTime?) we will probably write a query that looks like this:

  EntityCollection/*IQueryable<Assignment>*/.SingleOrDefault(o => o.ValidTo == null);

This simplified example works just fine. But as the application grows we probably need the same logic for different tables and we want to refactor and separate the business-logic from the context of a specific entity to ensure we dont violate the golden DRY principle.

This time we will have to get the name of the column at runtime because the type is generic and therefore the column is obviously unknown at compile time.

I use reflection to get the column name at runtime. The code ended up looking like this:

  EntityCollection/*IQueryable<TEntity>*/.SingleOrDefault(o => typeof(TEntity).GetProperty(_efRangeEndPropName /* ValidTo or whatever... */).GetValue(o) == null);

Note: The line above will throw an exception!

An exception of type 'System.NotSupportedException' occurred in EntityFramework.SqlServer.dll but was not handled in user code

Additional information: LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object)' method, and this method cannot be translated into a store expression.

From what I understand about this exception Entity Framework can't parse this expression into a SQL-query. My current workaround is to load the objects in memory before filtering the data:

var entityList = /*IQueryable<TEntity>*/EntityCollection.ToList();
entityList.SingleOrDefault(o => typeof(TEntity).GetProperty(_efRangeEndPropName).GetValue(o) == null);

This code works. However - you guessed it - I'm still not happy. While this might be okay when running the query against small datasets, performance issues with larger datasets are unavoidable this way (the entire dataset has to be loaded in memory first!)

How do I need to structure the expression in a way Entity Framework "understands" how to translate it into a SQL query? I bet I am missing something very simple here...

Thanks in advance

Upvotes: 2

Views: 1447

Answers (1)

Ivan Stoev
Ivan Stoev

Reputation: 205629

How do I need to structure the expression in a way Entity Framework "understands" how to translate it into a SQL query?

Instead of reflection, you should build it using the Expression class methods:

var parameter = Expression.Parameter(typeof(TEntity), "o"); // o =>
var left = Expression.PropertyOrField(parameter, _efRangeEndPropName); // o.Property
var right = Expression.Constant(null, left.Type); // null
var condition = Expression.Equal(left, right); // o.Property == null
var predicate = Expression.Lambda<Func<TEntity, bool>>(condition, parameter);

You can put the above in a separate method if you need it in a more that one place. The result expression (predicate variable) is EF compatible, as you can see:

var result = EntityCollection.SingleOrDefault(predicate);

Upvotes: 5

Related Questions