2010-03-12 13 views
13

ho letto this article dove Ayende afferma NHibernate può (rispetto al EF 4):paging su un insieme pigro-caricato con NHibernate

  • Collection con lazy =”extra” - Pigro significa più che NHibernate si adatta alla le operazioni che potresti eseguire su parte superiore delle tue raccolte. Ciò significa che blog.Posts.Count non forzerà un carico dell'intera collezione, ma piuttosto creerebbe un “select count (*) dai post dove BlogId = 1” dichiarazione, e che blog.Posts. Contiene() allo stesso modo genera una singola query anziché pagando il prezzo del caricamento dell'intera collezione in memoria.
  • filtri di raccolta e collezioni di paging - questo consente di definire i filtri aggiuntivi (tra cui paging!) In cima ai vostri entità collezioni, il che significa che si può facilmente pagina attraverso i blog.Posts raccolta, e non hanno per caricare l'intera cosa in memoria.

Quindi ho deciso di mettere insieme un test case. Ho creato il modello cliché blog come una semplice dimostrazione, con due classi come segue:

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; } 
} 

miei file mapping simile a questa:

<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> 

Il mio banco di prova simile a questa:

 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); 
       } 
      } 
     } 

Utilizzando pigro = "extra", la chiamata a b.Posts.Count fa fare un SELECT COUNT(Id)... che è grande. Tuttavia, b.Posts.Skip(0).Take(5) prende tutti i post per Blog.Id =? Id, e quindi LINQ sul lato dell'applicazione sta solo prendendo i primi 5 dalla raccolta risultante.

Cosa dà?

+1

+1 per una buona domanda. Mi sono interrogato sulle funzionalità di caricamento pigro di NHibernate per un po 'di tempo ma non ho avuto il tempo di provarle. –

+1

forse la parte da LINQ a NHibernate riguardante i filtri di raccolta non è implementata – Jaguar

+0

Solo un avvertimento per l'utilizzo di lazy = "extra". Abbiamo iniziato a utilizzarlo sulla nostra applicazione e tutto ha funzionato fino a quando non ci siamo distribuiti su una macchina Sql Server 2000 (sempre il software approvato dal cliente). Poiché utilizziamo un guid come campi di identità, abbiamo iniziato a ricevere l'eccezione, "Impossibile eseguire il conteggio sull'identificatore univoco" (circa). Ho guardato il codice sorgente di NH e ho scoperto che era il carico extra pigro che sceglie la prima colonna chiave e 'seleziona il conteggio ()', che era il nostro campo GUID. –

risposta

9

Sono abbastanza sicuro (leggendo i commenti) che sta parlando di CreateFilter of ISession.

Si può fare di paging come questo (dalla documentazione 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(); 

Or (dalla documentazione 17.1.4):

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

Questo non è liscio come utilizzare il sistema Metodi .Linq. Immagino che un giorno si uniranno alla sintassi.

+0

questo è quello che faccio e funziona come pubblicizzato – Jaguar

+1

Hmm, sì. deludente, non liscio. Ma questo succederà. Grazie. – HackedByChinese

+0

Ciao, Sarebbe possibile postare l'esempio originale, con questa tecnica implementata? Grazie. – UpTheCreek

Problemi correlati