Filtro de Entity Framework “Expression <Func >”

Estoy intentando crear un método de filtro para Entity framework List y comprender mejor la Expression<Func<...

Tengo una función de prueba como esta.

 public IQueryable Filter(IEnumerable src, Expression<Func> pred) { return src.AsQueryable().Where(pred); } 

y si hago esto:

 context.Table.Filter(e => e.ID < 500); 

o esto:

 context.Table.Filter(e => e.SubTable.Where(et => et.ID  0 && e.ID < 500); 

todo funciona bien

Pero si hago esto:

 context.Table.Filter(e => e.SubTable.Filter(et => et.ID  0 && e.ID < 500); 

o esto:

 context.Table.Where(e => e.SubTable.Filter(et => et.ID  0 && e.ID < 500); 

Recibo un error LINQ to Entities does not recognize the method ...Filter...

¿Por qué funciona en un caso y no en el sumdor? ¿Qué debo cambiar en el filtro para que funcione con las tablas relacionadas? Prefiero mantenerme alejado de otras bibliotecas externas, ya que lo que quiero es aprender cómo funciona y poder utilizarlo en cualquier escenario en el futuro.

En los primeros dos casos, el filtro se ejecuta correctamente en la base de datos.

Jon y Tim ya explicaron por qué no funciona.

Suponiendo que el código de filtro dentro de Filter no es trivial, puede cambiar Filter para que devuelva una expresión que EF pueda traducir.

Supongamos que tienes este código:

 context.Table.Where(x => x.Name.Length > 500); 

Ahora puede crear un método, el devuelve esta expresión:

 Expression> FilterByNameLength(int length) { return x => x.Name.Length > length; } 

El uso sería así:

 context.Table.Where(FilterByNameLength(500)); 

La expresión que construyes dentro de FilterByNameLength puede ser arbitrariamente compleja siempre que puedas pasarla directamente a Where .

Es útil entender la diferencia entre Expression> y Func<> .

Una Expression e => e.ID < 500 almacena la información sobre esa expresión: que hay una T e , que está accediendo a la ID la propiedad, llamando al < operador con el valor int 500 . Cuando EF lo vea, podría convertirlo en algo como [SomeTable].[ID] < 500 .

A Func e => e.ID < 500 es un método equivalente a:

 static bool MyMethod(T e) { return e.ID < 500; } 

Se comstack como código IL que hace esto; no está diseñado para ser 'reconstituido' en una consulta SQL o cualquier otra cosa, solo se ejecuta.

Cuando EF toma su Expression , debe entender cada parte de ella, porque la usa para construir una consulta SQL. Está progtwigdo para saber qué significa el método Where existente. No sabe lo que significa su método de Filter , a pesar de que es un método trivial, por lo que simplemente se da por vencido.

¿Por qué funciona en un caso y no en el sumdor?

Porque EF realmente no “sabe” acerca de su método de Filter . No comprende lo que se supone que debe hacer, por lo que no sabe cómo traducirlo a SQL. Compare eso con Where etc, que comprende.

La versión donde la llamas directamente en la tabla inicial funciona porque de esa manera no terminas con un árbol de expresiones que contiene una llamada a Filter ; simplemente llama directamente a Filter , que a su vez crea una consulta … pero una que EF entiende

Me sorprendería mucho si pudieras encontrar la manera de hacer que tu método de Filter funcione dentro de una consulta de EF … pero ya dijiste que usar Where funciona de todos modos, entonces ¿por qué usar Filter ? Usaría la versión Where , o mejor aún, usaría Any overload que toma un predicado:

 context.Table.Filter(e => e.SubTable.Any(et => et.ID < 500) && e.ID < 500);