2010-05-05 14 views
14

Tengo un objeto raíz que tiene una propiedad que es una colección.Colección NHibernate IQueryable como propiedad de la raíz

Por ejemplo:

I have a Shelf object that has Books. 

// Now 
public class Shelf 
{ 
    public ICollection<Book> Books {get; set;} 
} 

// Want 
public class Shelf 
{ 
    public IQueryable<Book> Books {get;set;} 
} 

Lo que quiero lograr es devolver una colección que es IQueryable para que pueda funcionar de paginación y filtrado fuera de la recogida directamente del padre.

var shelf = shelfRepository.Get(1); 

var filtered = from book in shelf.Books 
       where book.Name == "The Great Gatsby" 
       select book; 

quiero tener que consulta ejecutada específicamente por NHibernate y no un obtener toda cargar toda una colección y luego analizarlo en la memoria (que es lo que sucede actualmente cuando uso ICollection).

El razonamiento detrás de esto es que mi colección podría ser enorme, decenas de miles de registros y una consulta de obtención de todo podría arruinar mi base de datos.

Me gustaría hacer esto implícitamente para que cuando NHibernate vea un IQueryable en mi clase, sepa qué hacer.

He consultado el proveedor LINQ de NHibernate y actualmente estoy tomando la decisión de tomar grandes colecciones y dividirlas en su propio repositorio para que pueda hacer llamadas explícitas para el filtrado y la paginación.

LINQ To SQL ofrece algo similar a lo que estoy diciendo.

Respuesta

12

He estado tratando de encontrar una solución para un problema similar.

Puede filtrar colecciones de una entidad usando ISession.FilterCollection. Esto crea un IQuery adicional donde puede contar, ingresar, agregar criterios, etc.

Así, por ejemplo (mi consulta en FilterCollection puede ser un poco apagado, pero usted debe tener una idea):

ISession session = GetSession(); 
var shelf = session.Get<Shelf>(id); 
var books = session.FilterCollection(shelf.Books, "where Name = :title").SetString("title", "The Great Gatsby").List<Book>(); 

Hay un problema con eso, sin embargo:

  1. El el consumidor que ejecuta el código necesita acceder a ISession.CreateFilter, o necesita para crear un método en su repositorio que toma una propiedad, una consulta y sus argumentos de consulta (así como cualquier paginación u otra información de ). No es realmente la cosa más sexy en el planeta.
  2. No es el LINQ que deseaba.

Desafortunadamente, no creo que haya manera de obtener lo que desea de forma inmediata con NHibernate. Se podría fingir, si quería probar, pero parecen caer de plano a mí:

agregar un método o propiedad que bajo las sábanas devuelve un LINQ to NHibernate IQueryable para esta plataforma:

public IQueryable<Book> FindBooks() { 
    return Resolver.Get<ISession>().Linq<Book>().Where(b => b.Shelf == this); 
} 

donde alguien podría consumir que de esta manera:

var shelf = ShelfRepo.Get(id); 
var books = (from book shelf.FindBooks() 
      where book.Title == "The Great Gatsby" 
      select book); 

qué asco! Estás desangrando tus necesidades de persistencia a través de tu modelo de dominio. Tal vez usted podría hacer que sea un poco menos peor por tener un repositorio emiten IQueryable, que en tiempo de ejecución es en realidad LINQ to NHibernate:

public IQueryable<Book> FindBooks() { 
    return Resolver.Get<IRepository<Book>>().CreateQuery().Where(b => b.Shelf == this); 
} 

Aún bastante pesada.

Crear su propio tipo de colección personalizada (y, potencialmente, una implementación IQueryable) que envuelve un campo privado de los libros reales, y mapa NHibernate a ese campo. Sin embargo, puede ser una tarea difícil conseguir que trabaje con ISession.CreateFilter. Debe considerar "descubrir" la sesión actual, convirtiendo la expresión LINQ en algo que pueda usar en CreateFilter, etc. Además, su lógica comercial aún depende de NHibernate.

Nada realmente satisface en este momento. Hasta que NHibernate pueda hacer LINQ sobre una colección por usted, parece que es mejor que solo consulte el repositorio de su libro normalmente, como ya se ha sugerido, incluso si no parece tan atractivo o óptimo.

+0

Gracias por ser tan exhaustivo en su explicación. He pensado en las cosas que has dicho, y estoy de acuerdo con todos los puntos que hagas. Me pregunto si NHibernate 3 solo lo va a apoyar de esta forma. –

1

Tal vez deberías probar Nhibernate Linq. Permite que utiliza el IQueryable y hacer cosas como:

Session.Linq<Book>().Where(b => b.Name == "The Great Gatsby"); 
+0

"He consultado el proveedor NHibernates Linq y actualmente estoy tomando la decisión de tomar grandes colecciones y dividirlas en su propio repositorio para que pueda hacer llamadas explícitas para el filtrado y la búsqueda". –

+2

@Khalid - No sé por qué esto no te ayuda. Es exactamente lo que solicitó – Dann

+0

Mi pregunta era "¿Cómo hago que NHibernate se asigne a una propiedad IQueryable?" Ya sé sobre NHibernate Linq. Esta es una respuesta a una pregunta que nunca hice y es por eso que la he votado. –

3

tiendo a pensar de esta manera:

raíces agregados son límites de consistencia, por lo que si la plataforma tiene que cumplir algún tipo de políticas de consistencia en los libros contiene, entonces debería ser una raíz agregada. Y en tal caso, debe contener un conjunto/colección de libros.

Si no necesita exigir coherencia de ningún modo desde el estante a los libros, entonces consideraría eliminar la propiedad set/collection y mover esas consultas a un repositorio.

Además, como la paginación y el filtrado probablemente no tienen nada que ver con la lógica de su dominio, lo más probable es que sea para la presentación. Entonces consideraría hacer una vista especial en lugar de agregar facillities de presentación a mis repositorios.

p. Ej.

var result = Queries.FindBooksByShelf(shelfId,pageSize); 

Dicha consulta podría volver proyecciones y/o ser optimizado en SQL sin formato, etc. Lo más probable es específico para un determinado punto de vista o informe en su interfaz gráfica de usuario. De esta manera, su dominio se centrará únicamente en los conceptos de dominio.

Cuestiones relacionadas