2010-03-12 9 views
13

I leer this article donde Ayende afirma NHibernate puede (en comparación con EF 4):Paging sobre una colección-lazy cargado con NHibernate

  • Collection con lazy =”extra” - Lazy adicional significa que NHibernate se adapta a las operaciones que podría ejecutar en parte superior de sus colecciones. Eso significa que blog.Posts.Count no forzará una carga de toda la colección, pero en lugar crearía un “select count (*) de mensajes donde blogid = 1” declaración, y que blog.Posts. Contiene() también dará como resultado una sola consulta en lugar de pagando el precio de cargar toda la colección en la memoria.
  • filtros de recogida y colecciones paginados - esto le permite a definir filtros adicionales (incluyendo paginación!) En la parte superior de sus entidades colecciones, lo que significa que se puede página fácilmente a través de los blog.Posts colección, y no tener cargar el entero en la memoria.

Así que decidí armar un caso de prueba. He creado el modelo cliché Blog como una simple demostración, con dos clases de la siguiente manera:

public class Blog 
{ 
    public virtual int Id { get; private set; } 
    public virtual string Name { get; set; } 

    public virtual ICollection<Post> Posts { get; private set; } 

    public virtual void AddPost(Post item) 
    { 
     if (Posts == null) Posts = new List<Post>(); 
     if (!Posts.Contains(item)) Posts.Add(item); 
    } 
} 

public class Post 
{ 
    public virtual int Id { get; private set; } 
    public virtual string Title { get; set; } 
    public virtual string Body { get; set; } 
    public virtual Blog Blog { get; private set; } 
} 

Mis asignaciones de archivos tener este aspecto:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true"> 
    <class xmlns="urn:nhibernate-mapping-2.2" name="Model.Blog, TestEntityFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="Blogs"> 
    <id name="Id" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 
     <column name="Id" /> 
     <generator class="identity" /> 
    </id> 
    <property name="Name" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 
     <column name="Name" /> 
    </property> 
    <property name="Type" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 
     <column name="Type" /> 
    </property> 
    <bag lazy="extra" name="Posts"> 
     <key> 
     <column name="Blog_Id" /> 
     </key> 
     <one-to-many class="Model.Post, TestEntityFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> 
    </bag> 
    </class> 
</hibernate-mapping> 

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true"> 
    <class xmlns="urn:nhibernate-mapping-2.2" name="Model.Post, TestEntityFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="Posts"> 
    <id name="Id" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 
     <column name="Id" /> 
     <generator class="identity" /> 
    </id> 
    <property name="Title" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 
     <column name="Title" /> 
    </property> 
    <property name="Body" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> 
     <column name="Body" /> 
    </property> 
    <many-to-one class="Model.Blog, TestEntityFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="Blog"> 
     <column name="Blog_id" /> 
    </many-to-one> 
    </class> 
</hibernate-mapping> 

Mi caso de prueba es como la siguiente:

 using (ISession session = Configuration.Current.CreateSession()) // this class returns a custom ISession that represents either EF4 or NHibernate 
     { 
      blogs = (from b in session.Linq<Blog>() 
         where b.Name.Contains("Test") 
         orderby b.Id 
         select b); 

      Console.WriteLine("# of Blogs containing 'Test': {0}", blogs.Count()); 
      Console.WriteLine("Viewing the first 5 matching Blogs."); 

      foreach (Blog b in blogs.Skip(0).Take(5)) 
      { 
       Console.WriteLine("Blog #{0} \"{1}\" has {2} Posts.", b.Id, b.Name, b.Posts.Count); 
       Console.WriteLine("Viewing first 5 matching Posts."); 

       foreach (Post p in b.Posts.Skip(0).Take(5)) 
       { 
        Console.WriteLine("Post #{0} \"{1}\" \"{2}\"", p.Id, p.Title, p.Body); 
       } 
      } 
     } 

Usando lazy = "extra", la llamada a b.Posts.Count hace un SELECT COUNT(Id)... que es genial. Sin embargo, b.Posts.Skip(0).Take(5) solo toma todas las Publicaciones para Blog.Id =? Id, y luego LINQ en el lado de la aplicación solo está tomando los primeros 5 de la colección resultante.

¿Qué ofrece?

+1

+1 para una buena pregunta. Me he estado preguntando sobre las capacidades de carga lenta de NHibernates por un tiempo, pero no he tenido el tiempo para probarlas. –

+1

tal vez la parte LINQ to NHibernate con respecto a los filtros de recopilación no está implementada – Jaguar

+0

Solo una advertencia para usar lazy = "extra". Comenzamos a usar esto en nuestra aplicación y todo funcionó bien hasta que lo implementamos en una máquina Sql Server 2000 (aún el software aprobado por el cliente). Como usamos un guid como nuestros campos de identidad, comenzamos a recibir la excepción, 'No se puede contar el identificador único' (aprox). Miré el código fuente de NH y descubrí que era la carga extra diferida que elige la primera columna de clave y no 'selecciona recuento ()', que era nuestro campo GUID. –

Respuesta

9

Estoy bastante seguro (leyendo los comentarios) de que está hablando de CreateFilter of ISession.

Puede hacer la paginación como esto (de la documentación 13.13):

Collections are pageable by using the IQuery interface with a filter: 

IQuery q = s.CreateFilter(collection, ""); // the trivial filter 
q.setMaxResults(PageSize); 
q.setFirstResult(PageSize * pageNumber); 
IList page = q.List(); 

O (de los documentos 17.1.4):

s.CreateFilter(lazyCollection, "").SetFirstResult(0).SetMaxResults(10).List(); 

Eso no es tan suave como con el Sistema .Linq métodos. Supongo que se unirán a la sintaxis en algún momento también.

+0

eso es lo que hago y funciona como se anuncia – Jaguar

+1

Hmm, sí. decepcionantemente poco elegante. Pero, eso servirá. Gracias. – HackedByChinese

+0

Hola, ¿sería posible publicar el ejemplo original con esta técnica implementada? Gracias. – UpTheCreek