2011-08-11 22 views
6

Biorąc pod uwagę to:Czy NHibernate FetchMany na więcej niż dwóch stołach jest zepsuty?

namespace TheEntities 
{ 
    [DataContract(IsReference=true)]  
    public class Question 
    { 
     [DataMember] public virtual int QuestionId { get; set; } 
     [DataMember] public virtual string Text { get; set; } 
     [DataMember] public virtual string Poster { get; set; } 

     [DataMember] public virtual IList<QuestionComment> Comments { get; set; } 
     [DataMember] public virtual IList<Answer> Answers{ get; set; } 



     [DataMember] public virtual byte[] RowVersion { get; set; } 
    } 

    [DataContract] 
    public class QuestionComment 
    { 
     [DataMember] public virtual Question Question { get; set; }   

     [DataMember] public virtual int QuestionCommentId { get; set; } 
     [DataMember] public virtual string Text { get; set; } 
     [DataMember] public virtual string Poster { get; set; } 
    } 


    [DataContract(IsReference = true)] 
    public class Answer 
    { 
     [DataMember] public virtual Question Question { get; set; } 

     [DataMember] public virtual int AnswerId { get; set; } 
     [DataMember] public virtual string Text { get; set; } 
     [DataMember] public virtual string Poster { get; set; } 

     [DataMember] public virtual IList<AnswerComment> Comments { get; set; } 

    } 

    [DataContract] 
    public class AnswerComment 
    { 
     [DataMember] public virtual Answer Answer { get; set; } 

     [DataMember] public virtual int AnswerCommentId { get; set; } 
     [DataMember] public virtual string Text { get; set; } 
     [DataMember] public virtual string Poster { get; set; } 
    } 

} 

Entity Framework nie produkują duplikaty obiektów na Odpowiedź, QuestionComment, AnswerComment podczas NHibernate robi.

public Question OpenQuestion(int id) 
{ 
    var repo = QuestionRepository; 

    var query = repo.All.Where(y => y.QuestionId == id); 

    if (QuestionRepository.GetType() == typeof(EfRepository<Question>)) 
    {     
     query = query 
       .Include("Answers") 
        .Include("Answers.Comments") 
       .Include("Comments"); 

     return query.Single(); 
    } 
    else if (QuestionRepository.GetType() == typeof(NhRepository<Question>)) 
    {     
     // kinda sad, produces duplicate objects 
     query = query 
       .FetchMany(x => x.Answers) 
        .ThenFetchMany(x => x.Comments) 
       .FetchMany(x => x.Comments); 
    } 
    else 
     throw new Exception("Something unsupported"); 

    return query.Single(); 
} 

produkuje to również duplikaty obiektów (głęboki trzy poziomy, stosując trzy relacje): (głęboko tylko dwa poziomy, ale za pomocą trzech relations)

query = query 
    .FetchMany(x => x.Answers) 
    .ThenFetchMany(x => x.Comments) 

ta produkuje także duplikaty obiektów:

query = query 
    .FetchMany(x => x.Answers) 
    .FetchMany(x => x.Comments); 

Nie powoduje to duplikowania obiektów, jednak szybkie ładowanie ma tylko dwa poziomy głębokości i dwie relacje, tj. Od pytania do odpowiedzi. W przypadku komentarzy, które należy zadać, oraz komentarzy do odpowiedzi, są one wykonywane w oddzielnym zapytaniu.

query = query 
    .FetchMany(x => x.Answers); 

Jeśli NHibernate może jedynie wykonywać swoje zadania dobrze na dwóch poziomach FetchMany tylko z dwóch relacji tylko, po co tworzących ThenFetchMany (używany na trzech poziomach, ale ma błąd, ma duplikatów obiektów)? W rzeczywistości, nawet FetchMany jest bezużyteczne, jeśli chcesz używać go również na trzech relacjach, tworzy również duplikaty obiektów.

Czy zespół NHibernate może być przeszkadzający w usunięciu funkcji ThenFetchMany, ponieważ nie działa poprawnie?

Nie ma błędu w moim mapowaniu, rzeczy działają prawidłowo (tzn. Nie tworzą duplikatów obiektów) po usunięciu strategii pobierania.

+0

możesz zrobić, co chcesz - dodaj '.TrasformUsing (Transformers.DistinctRootentity)'. to jest denerwujące. – Firo

+0

Jest to dostępne tylko w QueryOver, ale używam Linq (IQueryable) NH. Jaka jest metoda rozszerzenia NH Linq odpowiednik '.TransformUsing'? –

+0

Dzieje się tak, ponieważ Entity Framework tworzy zapytanie za pomocą niektórych UNION, podczas gdy NHibernate wykonuje JOIN. JOIN powoduje produkty kartezjańskie, a więc duplikaty. Strategia EF jest w tym przypadku lepsza, ponieważ pozwala uzyskać wiele informacji w jednym zapytaniu. – Sebazzz

Odpowiedz

6

uzyskać unikalne wyniki zrobić

do Linq:

.Distinct() 

dla QueryOver

.TrasformUsing(Transformers.DistinctRootentity) 

dla Kryteriów

.SetResulttransformer(Transformers.DistinctRootentity) 

EDIT: effectivly to niedociągnięcie NH, wyda iloczyn kartezjański, ale można poprawić

repo.All.Where(y => y.QuestionId == id) 
     .FetchMany(x => x.Answers) 
      .ThenFetchMany(x => x.Comments) 
     .Future() 

query = repo.All.Where(y => y.QuestionId == id) 
     .FetchMany(x => x.Comments) 

var result = query.AsEnumerable().Single(); 

zobaczyć http://ayende.com/blog/4367/eagerly-loading-entity-associations-efficiently-with-nhibernate

wygląda to nieco dziwne, ale należy zrobić

0

Spróbuj:

query.QueryOptions.RegisterCustomAction(c => c.SetResultTransformer(new DistinctRootEntityResultTransformer())); 
+0

Jest to dostępne tylko w wersji NH 2.0 Linq. Na NH 3.0 Linq go brakuje –

+0

LINQ nie brakuje w NH3.0. Ma własnego dostawcę LINQ. Dostęp do niego odbywa się przez .Query. Możesz użyć Transformatora na QueryOver. Jednak nigdy nie powinieneś używać FetchMany więcej niż jeden raz w jednym zapytaniu. – Phill

+0

Tak, wiem, że NHibernate 3 ma wsparcie LINQ, jest dostępny przez .Quer , na LINQ 2, to jest .Linq . W rzeczywistości od kiedy zacząłem używać NH, robię tylko zapytania przez HQL i Linq (pracuję teraz wyłącznie nad tym, nie lubię magicznego łańcucha HQL). Właśnie umieściłem '.Query ' we właściwości "Wszystkie" mojego interfejsu repozytorium. Co mam na myśli to "Na NH 3.0 Linq, brakuje", po prostu zapomniałem wstawić przecinek między słowem ** Linq ** i ** it ** ;-) ** It **, będącym: 'QueryOptions .RegisterCustomAction' –

Powiązane problemy