2010-12-30 33 views
8

Me tropecé con un extraño comportamiento LINQ to SQL. ¿Alguien puede arrojar algo de luz sobre esto?LINQ to SQL: reutilizar la expresión lambda

Quiero definir una expresión lambda y usarla en mi instrucción LINQ. El siguiente código funciona bien:

[...] 
Func<Table1, bool> lambda = x => x.Id > 1000; 
var result = dataContext.Table1s.Where(lambda); 
[...] 

Pero cuando trato de usar mi expresión lambda en un comunicado en una tabla asociada

[...] 
Func<Table1, bool> lambda = x => x.Id > 1000; 
var result = dataContext.Table2s.Where(x => x.Table1s.Any(lambda)); 
[...] 

me sale una excepción:

Unsupported overload used for query operator 'Any'. 

Pero, y esto no lo entiendo: Funciona bien cuando puse mi lambda directamente en la consulta:

[...] 
var result = dataContext.Table2s.Where(x => x.Table1s.Any(y => y.Id > 1000)); 
[...] 

¿POR QUÉ?

Gracias.

+0

Intenta usar 'var lamda = x => x.Id> 1000;'. No sé, ayudará, pero podría ... – Alxandr

+0

@Alxandr - eso no es legal en realidad. Las expresiones de Lambda se pueden compilar a 'Func <>' o 'Expression >' y en su ejemplo el compilador no podrá decir cuál desea y arrojará un error. –

Respuesta

18

Bien, aquí está la oferta: dataContext.Table1s es del tipo IQueryable<T>. IQueryable<T> define Where y Any métodos que toman un predicado de tipo Expression<Func<T, bool>>. El contenedor Expression<> es fundamental, ya que esto es lo que permite a LINQ to SQL traducir su expresión lambda a SQL y ejecutarla en el servidor de la base de datos.

Sin embargo, IQueryable<T> también incluye IEnumerable<T>. IEnumerable<T> también define Where y Any métodos, pero la versión IEnumerable toma un predicado de tipo Func<T, bool>. Como esta es una función compilada y no una expresión, no se puede traducir a SQL. Como resultado, este código ...

Func<Table1, bool> lambda = x => x.Id > 1000; 
var result = dataContext.Table1s.Where(lambda); 

... tirará de cada registro de Table1s en la memoria, y luego filtrar los registros en la memoria. Funciona, pero son realmente malas noticias si tu mesa es grande.

Func<Table1, bool> lambda = x => x.Id > 1000; 
var result = dataContext.Table2s.Where(x => x.Table1s.Any(lambda)); 

Esta versión tiene dos expresiones lambda. El segundo, que se transfiere directamente al Where, es un Expression que incluye una referencia a Func. No puede mezclar los dos, y el mensaje de error que está recibiendo le dice que la llamada al Any está esperando un Expression pero está pasando un Func.

var result = dataContext.Table2s.Where(x => x.Table1s.Any(y => y.Id > 1000)); 

En esta versión, el lambda interno automáticamente se está convirtiendo en un Expression porque esa es la única opción si desea que su código para ser transformado en SQL LINQ a SQL. En los otros casos, está forzando que la lambda sea Func en lugar de Expression, en este caso no, así que funciona.

¿Cuál es la solución? En realidad es bastante simple:

Expression<Func<Table1, bool>> lambda = x => x.Id > 1000; 
+1

+1 muy buena explicación. –

+0

Gracias. Lamentablemente no se compilará, el error del compilador es: Argumento 2: no se puede convertir de 'System.Linq.Expressions.Expression >' a 'System.Func ' –

+1

Ese mensaje de error parece indicar que 'invocando 'Where' o' Any' en un objeto que implementa IEnumerable pero no IQueryable, porque las versiones [IEnumerable] (http://msdn.microsoft.com/en-us/library/bb534803.aspx) esperan un Func, donde las versiones [IQueryable] (http://msdn.microsoft.com/en-us/library/bb548547.aspx) esperan una Expresión. Si no acepta una Expresión, es posible que el objeto no sea IQueryable ... –

0

¿Se refiere al mismo espacio de nombre Table1? En el primer ejemplo que está consultando contra los objetos Table1 que están directamente debajo de dataContext, en el segundo ejemplo está consultando contra los objetos Table1 que son una propiedad de los objetos Table2, y en el último ejemplo está usando un anónimo función que soluciona el problema.

me gustaría ver el tipo de los objetos Table1 que es una propiedad de un objeto Table2 y compararlo con un objeto Table1 que está conectado directamente a la dataContext. Supongo que difieren y su expresión lambda está utilizando el tipo de objeto que está conectado al dataContext.