2009-08-07 15 views
5

mam zmaga się z tym przez chwilę, a nie wydaje się zrozumieć to ...Częściowo Wypełnianie kolekcja dziecięca z NHibernate

Mam BlogPost klasy, który posiada kolekcję Comments , a każdy z komentarzy ma pole DatePosted.

Co muszę zrobić, to zapytanie o BlogPost i odesłać go z częściowo załadowanym Comments kolekcji, powiedzmy wszystkie komentarze zamieszczone w dniu 1 sierpnia 2009 r

ja mam takie zapytanie:

BlogPost post = session.CreateCriteria<BlogPost>() 
    .Add(Restrictions.Eq("Id", 1)) 
    .CreateAlias("Comments", "c") 
    .Add(Restrictions.Eq("c.DatePosted", new DateTime(2009, 8, 1))) 
    .UniqueResult<BlogPost>(); 

Po uruchomieniu tej kwerendy i sprawdzeniu wygenerowanego sql, najpierw uruchamia zapytanie do tabeli BlogPost, dołączając do tabeli Comment z poprawnym ograniczeniem daty, a następnie uruchamia drugie zapytanie właśnie na tabeli Comment, która zwraca wszystko.

Wynik to kolekcja Comments z klasy BlogPost całkowicie wypełniona!

Co robię źle?

Mam próbki kodu, jeśli ktoś potrzebuje więcej informacji ...!

Odpowiedz

0

Naprawdę nie robisz nic złego - hibernacja po prostu nie działa w ten sposób.

Jeśli przechodzisz z BlogPost do komentarzy, Hibernacja zapełni komentarze w oparciu o określone przez ciebie odwzorowanie powiązania, a nie zapytanie użyte do pobrania BlogPost. Prawdopodobnie twoje mapowanie polega właśnie na połączeniu na kluczowej kolumnie. Możesz skorzystać z numeru filter to get the effect, którego szukasz. Ale myślę, że to będzie nadal pobierać wszystkie komentarze, a następnie zrobić filtr końcowy.

Mówiąc prościej, wystarczy zapytać o to, co chcesz:

List<Comments> comments = session.CreateCriteria<BlogPost>() 
      .Add(Restrictions.Eq("Id", 1)) 
      .CreateAlias("Comments", "c") 
      .Add(Restrictions.Eq("c.DatePosted", new DateTime(2009, 8, 1))) 
      .list(); 

to będzie w rzeczywistości zamian tylko komentarze z określoną datą. jeśli to sprawia, że ​​czujesz się lepiej, można ustawić je tak:

post.setComments(comments); //having already retreived the post elsewhere 

Byłem też zaskoczony tym zachowanie, kiedy pierwszy raz spotkałem go. Wydaje się, że to błąd, ale powiedziano mi o nim po projekcie.

+0

NH nigdy nie wykonuje filtrowania końcowego. –

0

Dziękuję za odpowiedź, myślę, że trochę rozumiem, dlaczego jest to zgodne z projektem, ale pomyślałem, że będzie wbudowana metoda, aby to umożliwić, twoje rozwiązanie działa, ale wydaje się być trochę hackerem!

Mój problem polega na tym, że kolekcja dla dzieci jest OGROMNA, jeśli nie jest filtrowana (przykład podałem postów i komentarzy miała chronić nazwiska niewinnych!) I jest teraz sposób, w jaki mogę wyciągnąć wszystkie dane za każdym razem .

Uruchomiłem Sql Profiler na tym i nadal pobiera wszystkie dane z powrotem. Kiedy uruchamiam następujący kod, pierwsze zapytanie robi to, czego się spodziewasz, tylko jeden post wraca, ale zaraz po wykonaniu drugiego zapytania, dwa zapytania trafiają do bazy danych, pierwsza pobiera przefiltrowane komentarze (bingo!) , a następnie sekundę do wypełnienia właściwości post.Comments ze wszystkimi komentarzami, właśnie tego próbuję uniknąć!

 var post = session.CreateCriteria<BlogPost>() 
      .Add(Restrictions.Eq("Id", 1)) 
      .UniqueResult<BlogPost>(); 

     var comments = session.CreateCriteria<Comment>() 
      .Add(Restrictions.Eq("BlogPostId", 1)) 
      .Add(Restrictions.Eq("DatePosted", new DateTime(2009, 8, 1))) 
      .List<Comment>(); 

     post.Comments = comments; 

to jest bardzo dziwne, nie tak jak jestem wyliczanie na listę post.Comments, więc dlaczego jest zapełnianie go ?! oto moje zajęcia i mapy:

public class BlogPostMap : ClassMap<BlogPost> 
{ 
    public BlogPostMap() 
    { 
     Id(b => b.Id); 
     Map(b => b.Title); 
     Map(b => b.Body); 
     HasMany(b => b.Comments).KeyColumnNames.Add("BlogPostId"); 
    } 
} 
public class CommentMap : ClassMap<Comment> 
{ 
    public CommentMap() 
    { 
     Id(c => c.Id); 
     Map(c => c.BlogPostId); 
     Map(c => c.Text); 
     Map(c => c.DatePosted); 
    } 
} 

public class BlogPost 
{ 
    public virtual int Id { get; set; } 
    public virtual string Title { get; set; } 
    public virtual string Body { get; set; } 
    public virtual IList<Comment> Comments { get; set; } 
} 
public class Comment 
{ 
    public virtual int Id { get; set; } 
    public virtual int BlogPostId { get; set; } 
    public virtual string Text { get; set; } 
    public virtual DateTime DatePosted { get; set; } 
} 

jakieś pomysły?

+0

Użyj SetResultTransformer. Zobacz moją odpowiedź. –

0

Zgadzam się, że ręczne wypełnianie kolekcji wydaje się być hakiem.

Zamiast tego można użyć niestandardowego programu ładującego. Coś takiego:

<query name="loadComments"> 
<return alias="comments" class="Comment"/> 
<load-collection alias="comments" role="Post.comments"/> 
from Comments c where c.Id = ? and c.DatePosted = SYSDATE 
</query> 

Możesz również użyć zapytania sql, jeśli chcesz mieć większą kontrolę. Czasami schylałem się do pisania niestandardowych ładowarek, gdy nie mogłem uzyskać hibernacji, aby wygenerować zapytanie, które chciałem. W każdym razie, nie wiem, dlaczego nie myślałem o tym w pierwszej kolejności.

2

Jest transformator wynik tego, patrz the documentation.

Cytat:

Zauważmy, że zbiory kocięta odbyło przez instancje Cat zwracanych przez dwóch poprzednich zapytaniach nie są wstępnej filtracji według kryteriów! Jeśli chcesz, aby odzyskać tylko kociaki, które spełniają kryteria , musisz użyć SetResultTransformer(CriteriaUtil.AliasToEntityMap).

IList cats = 
sess.CreateCriteria(typeof(Cat)) 
    .CreateCriteria("Kittens", "kt") 
     .Add(Expression.Eq("Name", "F%")) 
    .SetResultTransformer(CriteriaUtil.AliasToEntityMap) 
    .List(); 

Można również użyć filtrów aktywowany za pomocą session.EnableFilter(name). Jest to similar question here.

0

Dokonaj leniwego zbierania komentarzy, aby hibernacja nie była pobierana, gdy pobierasz BlogPost. Następnie użyj filtra w kolekcji Komentarze.

 
comments = session.CreateFilter(blogPost.Comments, ...).List();