7

Mam kwerendę NHibernate Linq, która nie działa tak, jak bym się spodziewał.NHibernate tworzy kod SQL ze złym złączem

Problem wydaje się wynikać z użycia kolumny z zerową wartością int z lewej złączonej tabeli w klauzuli where. To powoduje, że sprzężenie zachowuje się jak wewnętrzne sprzężenie.

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) 

SQL produkowane przez to wygląda (od przyłączyć roku - nie ma potrzeby, aby zobaczyć wszystkie wybiera)

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

więc część, która jest przyczyną problemu jest linia 5 powyższy fragment. Jak widać, NHibernate próbuje dołączyć do "starej szkoły" w moim widoku WorkflowCaseView. Powoduje to, że zapytanie wyklucza inne ważne akcje, które nie mają identyfikatora CaseId w tabeli WorkAllAction.

Czy ktoś mógłby wyjaśnić, dlaczego NHibernate pisze ten SQL i jak mogę go zachęcić do stworzenia lepszego zapytania?

Dzięki!

Ważne bity od WorkflowActionMap

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

Ważne bity od WorkflowCaseViewMap

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

Patrząc na to, zastanawiam się, czy powinienem mieć hasMany wraca inną drogą ...

EDYTOWAĆ. Nie wydaje się, aby pomóc

Odpowiedz

0

Zaimplementowałem to sprzężenie przy użyciu procedury przechowywanej. Mam nadzieję, że NHibernate wkrótce naprawi ten błąd.

3

Myślę, że trzeba zmienić klauzulę Where do tego:

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

Z aktualnej Where klauzuli Ci powiedzieć NHibernate, że zawsze będzie CaseView, bo bezwarunkowego dostępu do jego nieruchomości. Na podstawie tych informacji NHibernate optymalizuje zapytanie z left outer join do inner join (do którego dołączono "old-school").

+0

Dzięki za szybką odpowiedź. Próbowałem twojej sugestii (i wymyśliłem to samo, używając tylko .HasValue zamiast! = Null) i to nie pomogło. –

+0

@MarkWithers: Co się stanie, jeśli całkowicie usuniesz tę część z klauzuli "Where"? W jaki sposób mapujesz między 'WorkflowCaseAction' i' WorkflowCaseView'? –

+0

Po usunięciu "|| x.CaseView.MooseUserId == userId" z klauzuli where wywołuje akcje bez rekordów sprawy i używa trzech lewych złączeń zewnętrznych, tak jak oczekiwałbym. –

0

Spróbuj użyć Fluent NHibernate. Coś jak poniżej powinien dostać w dobrym parku kulowym:

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

pamiętać, mam specjalną klasę, która rozciąga FluentSessionManager.GetSession(), gdzie mogę połączyć go bezpośrednio z prostym klasy pomocnika lub na zasadzie strona po stronie. Twoje ustawienia FluentSessionManager mogą się znacznie różnić. Ale ostatecznie w ".CreateCriteria() ..." twój kod i mój powinien się zgadzać. Zakładając, że "WorkflowAction" jest tabelą, do której wywoływane jest zapytanie.