2012-01-04 20 views
15

Tengo algunos problemas para entender las diferencias entre cómo funcionan las Expresiones y los Funcs. Este problema se presentó cuando alguien cambia una firma de método de:Confundido acerca de pasar los argumentos Expresión vs. Func

public static List<Thing> ThingList(Func<Thing, bool> aWhere) 

Para

public static List<Thing> ThingList(Expression<Func<Thing, bool>> aWhere) 

Qué me rompió el código de llamada. El código de llamada de edad (que funcionaba) era la siguiente:

 ... 
     object y = new object(); 
     Func<Thing, bool> whereFunc = (p) => p == y; 
     things = ThingManager.ThingList(whereFunc); 

el nuevo código (que no funciona) tiene el siguiente aspecto:

 ... 
     object x = new object(); 
     Expression<Func<Thing, bool>> whereExpr = (p) => p == x; 
     things = ThingManager.ThingList(whereExpr); 

Esta falla dentro ThingList (...) el la línea de la utilización de la expresión:

 var query = (from t in context.Things.Where(aWhere) 
     ... 

Con el error de ejecución:

Unable to create a constant value of type 'System.Object'. Only primitive types ('such as Int32, String, and Guid') are supported in this context. 

Este ejemplo es artificial, pero supongo que tiene algo que ver con que la variable de objeto local x no se "copie" correctamente en la expresión.

¿Alguien puede explicar cómo manejar esta situación en general, y por qué funciona el Func pero el Expression no?

Respuesta

11

La razón para el cambio es casi seguro que era para "empujar" la evaluación de su predicado en el almacén subyacente, que respalda su context. En lugar de traer todos Things a la memoria y luego usar Func<Thing,bool> para decidir cuáles conservar, el autor de la API modificada decidió usar IQueryable, y necesitó un Expression<Func<Thing,bool>> para eso.

Tiene razón sobre el origen del error: a diferencia de los predicados en memoria, IQueryable no puede usar objetos que no conoce, p. Ej. instancias arbitrarias de object.

Lo que debe hacer es cambiar la expresión para evitar hacer referencia a objetos de tipos de datos no admitidos por su almacén de datos de destino (supongo que la expresión finalmente entra en un Entity Framework o un contexto Linq2Sql). Por ejemplo, en lugar de decir

object x = new object(); 
Expression<Func<Thing, bool>> whereExpr = (p) => p == x; 
things = ThingManager.ThingList(whereExpr); 

que debe decir

Thing x = new Thing {id = 123}; 
Expression<Func<Thing, bool>> whereExpr = (p) => p.id == x.id; 
things = ThingManager.ThingList(whereExpr); 

(el almacén de respaldo casi seguro que entiende enteros)

+0

sí, que se abre paso a un marco de la entidad. Supongo que tendré que crear dos métodos, uno para Expression y otro para que Func lo use cuando sea necesario. – Erix

6

La diferencia entre la expresión y Func se describe mejor en las respuestas aquí: Difference between Expression<Func<>> and Func<>

Una solución rápida para que esto funcione de nuevo sería para compilar la expresión de nuevo en un Func.

var query = (from t in context.Things.Where(aWhere.Compile()) 
Cuestiones relacionadas