Дублированные и ненужные объединения при использовании Linq в NHibernate - PullRequest
1 голос
/ 14 февраля 2012

По сути, я столкнулся с той же проблемой поставщика Linq в этом linq-to-nhibernate-производит-ненужных-соединениях

List<Competitions> dtoCompetitions;
dtoCompetitions = (from compset in session.Query<FWBCompetitionSet>()
                    where compset.HeadLine == true 
                    && compset.A.B.CurrentSeason == true
                   select (new Competitions
                           {
                                       CompetitionSetID = compset.CompetitionSetID,
                                       Name = compset.Name,
                                       Description = compset.Description,
                                       Area = compset.Area,
                                       Type = compset.Type,
                                       CurrentSeason = compset.A.B.CurrentSeason,
                                       StartDate = compset.StartDate
                            }
                    )).ToList();

, что приводит к дублированному соединению в его сгенерированном SQL

SELECT fwbcompeti0_.competitionsetid AS col_0_0_,
   fwbcompeti0_.name             AS col_1_0_,
   fwbcompeti0_.DESCRIPTION      AS col_2_0_,
   fwbcompeti0_.area             AS col_3_0_,
   fwbcompeti0_.TYPE             AS col_4_0_,
   fwbseason3_.currentseason     AS col_5_0_,
   fwbcompeti0_.startdate        AS col_6_0_
FROM   fwbcompetitionset fwbcompeti0_
       INNER JOIN A fwbcompeti1_
         ON fwbcompeti0_.competitionseasonid = fwbcompeti1_.competitionseasonid
       INNER JOIN A fwbcompeti2_
         ON fwbcompeti0_.competitionseasonid = fwbcompeti2_.competitionseasonid
       INNER JOIN B fwbseason3_
         ON fwbcompeti2_.seasonid = fwbseason3_.seasonid
WHERE  fwbcompeti0_.headline = @p0
       AND fwbseason3_.currentseason = @p1  

Обратите внимание на эти объединения, которые полностью дублированы и также влияют на производительность моего SQL Server.

       INNER JOIN A fwbcompeti1_
         ON fwbcompeti0_.competitionseasonid = fwbcompeti1_.competitionseasonid
       INNER JOIN A fwbcompeti2_
         ON fwbcompeti0_.competitionseasonid = fwbcompeti2_.competitionseasonid

Update1

В NHibernate 3.2эта ошибка LiNQ все еще действительна, и я не смог найти простое и разумное решение Linq.Поэтому я использовал QueryOver + JoinAlias ​​+ TransformUsing заканчивая работу, рабочие мне идеально подходят.

FWBCompetitionSet compset = null;
FWBCompetitionSeason compseason = null;
FWBSeason season = null;
IList<Competitions> dtoCompetitions;
dtoCompetitions = session.QueryOver<FWBCompetitionSet>(() => compset)
.JoinAlias(() => compset.FWBCompetitionSeason, () => compseason)
.JoinAlias(() => compseason.FWBSeason, () => season)
.Where(() => compset.HeadLine == true)  
.And(() => season.CurrentSeason == true)
.SelectList(
list => list
.Select(c => c.CompetitionSetID).WithAlias(() => compset.CompetitionSetID)
.Select(c => c.Name).WithAlias(() => compset.Name)
.Select(c => c.Description).WithAlias(() => compset.Description)
.Select(c => c.Area).WithAlias(() => compset.Area)
.Select(c => c.Type).WithAlias(() => compset.Type)
.Select(c => season.CurrentSeason).WithAlias(() => season.CurrentSeason)
.Select(c => c.StartDate).WithAlias(() => compset.StartDate)
)
.TransformUsing(Transformers.AliasToBean<Competitions>())
.List<Competitions>();

1 Ответ

3 голосов
/ 14 февраля 2012

Еще одна правка:

Думаю, я наконец узнал, что происходит. Похоже, что поставщик LINQ to NHibernate испытывает проблемы при перемещении ассоциаций от цели к исходной таблице и генерирует отдельное объединение каждый раз, когда он сталкивается с такой ассоциацией.

Поскольку вы не предоставляете свое сопоставление, я использовал сопоставление из linq-to-nhibernate-продукции-ненужный-объединения . Эта модель имеет Документ с одним заданием и многими TranslationUnits . Каждый TranslationUnit имеет множество Перевод сущностей.

Когда вы пытаетесь найти Перевод, основанный на задании, вы пересекаете ассоциации в обратном порядке, и поставщик LINQ генерирует несколько объединений: одно для Translation -> TranslationUnit и одно для TranslationUnit to Document.

Этот запрос будет генерировать избыточные объединения:

session.Query<TmTranslation>()
          .Where(x => x.TranslationUnit.Document.Job == job)
          .OrderBy(x => x.Id)
          .ToList();

Если вы измените порядок навигации на Document -> TranslationUnit -> Translation, вы получите запрос, который не создает никаких избыточных объединений:

var items=(from doc in session.Query<Document>()
        from tu in doc.TranslationUnits
            from translation in tu.Translations
       where doc.Job ==job                        
       orderby translation.Id
       select translation).ToList();

Учитывая эту причудливость, QueryOver кажется лучшим вариантом.

Предыдущее редактирование:

Я подозреваю, что виновником является compset.A.B.CurrentSeason . Первая объединенная таблица (fwbcompeti1_) возвращает A .B, в то время как следующие две (fwbcompeti2_ и fwbseason3_) используются для возврата A. B . Поставщик LINQ to NHibernate, похоже, не догадывается, что A нигде больше не используется и не может удалить его из сгенерированного оператора.

Попробуйте немного помочь оптимизатору, заменив CurrentSeason = compset.ABCurrentSeason на CurrentSeason = true из select , поскольку ваш оператор where возвращает только элементы с CurrentSeason == true.

РЕДАКТИРОВАТЬ: Я имею в виду, чтобы изменить запрос следующим образом:

List<Competitions> dtoCompetitions;
dtoCompetitions = (from compset in session.Query<FWBCompetitionSet>()
                   where compset.HeadLine == true 
                   && compset.A.B.CurrentSeason == true
                   select (new Competitions
                       {
                                   CompetitionSetID = compset.CompetitionSetID,
                                   Name = compset.Name,
                                   Description = compset.Description,
                                   Area = compset.Area,
                                   Type = compset.Type,
                                   CurrentSeason = true,
                                   StartDate = compset.StartDate
                        }
                )).ToList();

Я просто заменяю значение compset.A.B.CurrentSeason на true

...