2010-05-06 7 views
9

Tengo una interfaz llamada ICatalog como se muestra a continuación, donde cada ICatalog tiene un nombre y un método que devolverá elementos basados ​​en una función Predicate<Item>.Linq basado alternar genérico al predicado <T>?

public interface ICatalog 
{ 
    string Name { get; } 
    IEnumerable<Item> GetItems(Predicate<Item> predicate); 
} 

Una implementación específica de un catálogo se puede vincular a catálogos en varios formatos, como XML o una base de datos SQL.

Con un catálogo XML termino deserializando todo el archivo XML en la memoria, por lo que probar cada elemento con la función de predicado no agrega mucho más sobrecarga ya que ya está en la memoria.

Sin embargo, con la implementación de SQL prefiero no recuperar todo el contenido de la base de datos en la memoria, y luego filtrar los elementos con la función de predicado. En cambio, me gustaría encontrar una manera de pasar el predicado al servidor SQL, o de alguna manera convertirlo a una consulta SQL.

Parece un problema que se puede resolver con Linq, pero soy bastante nuevo. ¿Debería mi interfaz devolver IQueryable en su lugar? No me preocupa en este momento cómo implementar realmente una versión SQL de mi catálogo de IC. Solo quiero asegurarme de que mi interfaz lo permita en el futuro.

Respuesta

7

Rob ha indicado cómo puede hacer esto (aunque un enfoque LINQ más clásico podría tomar Expression<Func<Item,bool>>, y posiblemente devolver IQueryable<IFamily>).

La buena noticia es que si desea utilizar el predicado con LINQ a objetos (por su escenario XML) puede entonces sólo tiene que utilizar:

Predicate<Item> func = predicate.Compile(); 

o (para la otra firma):

Func<Item,bool> func = predicate.Compile(); 

y usted tiene un delegado (func) para probar sus objetos.

El problema, sin embargo, es que esto es una pesadilla a prueba de unidad - sólo se puede realmente integración prueba de ello.

El problema es que no se puede fingir de forma fiable (con LINQ-to-Objects) nada que implique tiendas de datos complejas; por ejemplo, lo siguiente será trabajar bien en sus pruebas de unidad, pero no va a funcionar "de verdad" en contra de una base de datos:

var foo = GetItems(x => SomeMagicFunction(x.Name)); 

static bool SomeMagicFunction(string name) { return name.Length > 3; } // why not 

El problema es que sólo algunas operaciones pueden ser traducidos a TSQL.Obtiene el mismo problema con IQueryable<T>; por ejemplo, EF y LINQ-to-SQL admiten operaciones diferentes en una consulta; incluso solo First() se comporta de manera diferente (EF exige que primero se ordene explícitamente, LINQ-to-SQL no lo hace).

Así que en resumen:

  • Puede trabajar
  • pensar cuidadosamente si usted quiere hacer esto; una interfaz de cuadro negro repositorio/servicio más clásico puede ser más comprobable
+0

Gracias por la respuesta Marc. Suena más complicado de lo que quisiera por lo que trato de hacer. ¿Qué quisiste decir con una "interfaz de depósito/servicio de caja negra más clásica"? ¿Puede dar un ejemplo? –

5

Usted no tiene que ir todo el camino y crear un IQueryable implementation

Si usted declara su método GetItems como:

IEnumerable<IFamily> GetItems(Expression<Predicate<Item>> predicate); 

Entonces su clase que implementa puede inspeccionar la expresión para determinar lo que se está preguntó.

Sin embargo, lea el artículo IQueryable, ya que explica cómo crear un visitante del árbol de expresiones, del que deberá crear una versión simple de.

+0

FYI: en mal estado el ejemplo original, que debe tener ambos utilizan tanto un IEnumerable y predicado no IEnumerable

Cuestiones relacionadas