7

Ho una query Linq di NHibernate che non funziona come mi aspetterei.NHibernate sta producendo SQL con un cattivo join

Il problema sembra derivare dall'utilizzo di una colonna int Nullabile da una tabella unita sinistra nella clausola where. Questo fa sì che il join si comporti come un join interno.

var list = this.WorkflowDiaryManager.WorkflowActionRepository.All 
    .Fetch(x => x.CaseView) 
    .Fetch(x => x.WorkflowActionType) 
    .ThenFetchMany(x => x.WorkflowActionPriorityList) 
    .Where(x => x.AssignedUser.Id == userId || x.CaseView.MooseUserId == userId) 

Lo SQL prodotta da questo appare come (dal punto di giunzione in poi - non è necessario per vedere tutte le seleziona)

from Kctc.WorkflowAction workflowac0_ 
left outer join Kctc.WorkflowCaseView workflowca1_ on workflowac0_.CaseId=workflowca1_.CaseId 
left outer join Kctc.WorkflowActionType workflowac2_ on workflowac0_.WorkflowActionTypeId=workflowac2_.WorkflowActionTypeId 
left outer join Kctc.WorkflowActionPriority workflowac3_ on workflowac2_.WorkflowActionTypeId=workflowac3_.WorkflowActionTypeId 
,Kctc.WorkflowCaseView workflowca4_ 
where workflowac0_.CaseId=workflowca4_.CaseId 
and ([email protected] or workflowca4_.[MooseUserId][email protected]); 
@p0 = 1087 [Type: Int32 (0)], 
@p1 = 1087 [Type: Int32 (0)] 

Così la parte che causa il problema è la linea 5 della il frammento sopra. Come puoi vedere, NHibernate sta provando a fare un join "old-school" sulla mia vista WorkflowCaseView. Questo fa sì che la query escluda azioni altrimenti valide che non hanno un CaseId nella tabella WorkflowAction.

Qualcuno potrebbe spiegare perché NHibernate stia scrivendo questo SQL e come potrei incoraggiarlo a produrre una query migliore?

Grazie!

bit importante dal WorkflowActionMap

bit
 Table("Kctc.WorkflowAction"); 
     Id(x => x.Id).GeneratedBy.Identity().Column("WorkflowActionId"); 
     References(x => x.WorkflowActionType).Column("WorkflowActionTypeId").Unique(); 
     References(x => x.CompletedBy).Column("CompletedBy"); 
     References(x => x.CaseView).Column("CaseId").Not.Update().Unique(); 
     References(x => x.AssignedUser).Column("AssignedUser"); 

importante dal WorkflowCaseViewMap

 Table("Kctc.WorkflowCaseView"); 
     Id(x => x.Id).Column("CaseId"); 
     Map(x => x.MooseUserId).Nullable(); 

Guardando a questo, mi chiedo se dovrei avere una hasMany tornare dall'altra parte ...

MODIFICARE. Non sembra aiutare

risposta

0

ho implementato questa join utilizzando una stored procedure. Speriamo che NHibernate risolverà presto questo bug.

3

Credo che hai bisogno di cambiare il vostro clausola Where a questo:

.Where(x => x.AssignedUser.Id == userId || 
     (x.CaseView != null && x.CaseView.MooseUserId == userId)) 

Con il vostro attuale Where clausola dici NHibernate che ci sarà sempre un CaseView, perché si accede incondizionatamente la sua proprietà. Sulla base di queste informazioni NHibernate ottimizza la query da un left outer join ad un inner join (che la "vecchia scuola" join è)

+0

Grazie per la risposta rapida. Ho provato il tuo suggerimento (e mi è venuta in mente la stessa cosa, usando solo .HasValue invece di! = Null) e non ha aiutato. –

+0

@MarkWithers: cosa succede se si rimuove completamente tale parte dalla clausola 'Where'? Com'è la tua mappatura tra 'WorkflowCaseAction' e' WorkflowCaseView'? –

+0

Se rimuovo "|| x.CaseView.MooseUserId == userId" dalla clausola where, riporta le azioni senza record del caso e utilizza tre join esterni a sinistra come mi aspetterei. –

0

Provare a utilizzare Fluent NHibernate. Qualcosa come il seguente dovrebbe arrivare nella giusta parco palla:

var List<WorkflowAction> = FluentSessionManager.GetSession().CreateCriteria<WorkflowAction>() 
     .SetFetchMode("CaseView", FetchMode.Eager) 
     .SetFetchMode("WorkflowActionType", FetchMode.Eager) 
     .SetFetchMode("WorkflowActionPriorityList", FetchMode.Eager) 
     .CreateAlias("AssignedUser", "au") 
     .CreateAlias("CaseView", "cv") 
     .Add(Expression.Or(Expression.Eq("au.Id", userId), Expression.Eq("cv.MooseUserId", userId))) 
     .List<WorkflowAction>(); 

Tenete a mente, ho una classe speciale che si estende FluentSessionManager.GetSession() dove posso chiamarlo direttamente con un semplice classe helper o su un pagina per pagina. La configurazione di FluentSessionManager potrebbe essere notevolmente diversa. Ma alla fine a ".CreateCriteria() ..." il tuo codice e il mio dovrebbero coincidere. Supponendo che "WorkflowAction" è la tabella a cui viene chiamata la query.