2015-09-01 5 views
1

Я пытаюсь получить старый запрос, переведенный в Nhibernate. Мы обновляем старый проект от Nhibernate 2 до последней версии. Я использую синтаксис QueryOver, поскольку Linq не был вариантом из-за сложности запросов (советы коллеги).Nhibernate Group By and Alias ​​To Bean

Я хочу запросить DB (Oracle), чтобы получить некоторые результаты, которые необходимо сгруппировать. В результате мне нужна сгруппированная коллекция моего DTO. Я также заметил, что nhibernate имеет проблемы с переводом на DTO со сложными свойствами (вложенными DTO) Чтобы исправить это, я нашел this тему. Это отлично работает, но я не поклонник магических струн ...

Я добавлю некоторые фрагменты кода о том, как мой вопрос с монстром смотрит на этот момент. Проблема в том, что я не могу понять, как добавить группу, не нарушая все остальное. Поэтому я хотел бы группировать объекты, но по-прежнему имею DTO в своих результатах. Что-то вроде:

ILookup<int,IEnumerable<NieuwePrintopdrachtenInfo>> 

Любая помощь будет радушна.

К сожалению переменные и классы в голландском -_-

SYNUITGAANDEBRIEF uitgaandebrief = null; 
SYNAANVRAAG joinedAanvraag = null; 
SYNDOSSIER joinedDossier = null; 
SYNVERBRUIKSADRES joinedVerbruiksAdres = null; 
SYNEAN joinedEan = null; 
SYNCTENERGIETYPE joinedEnergieType = null; 
SYNBRIEFBESTEMMELINGEN joinedBriefBestemmeling = null; 
SYNCTBRIEFTYPE joinedBriefType = null; 
SYNCTBRIEFSTATUS joinedBriefStatus = null; 
SYNCONTACTPERSOON joinedContactpersoon = null; 
SYNCTCONTACTPERSOONTYPE joinedBestemmelingType = null; 
SYNCTVERZENDMODUSTYPE joinedVerzendModus = null; 
SYNCTCONTACTPERSOONTYPE joinedContactpersoonType = null; 
SYNCTTAAL joinedContactpersoonTaal = null; 
SYNTOEWIJZVERBRUIKVERANT joinedVerbruiksVerantw = null; 
SYNCTPROFIELGROEP joinedProfielGroep = null; 

var baseQuery = SessionHandler.CurrentSession.QueryOver(() => uitgaandebrief) 
    .JoinAlias(() => uitgaandebrief.AANVRAAGCollection,() => joinedAanvraag) 
    .JoinAlias(() => joinedAanvraag.DOSSIER,() => joinedDossier) 
    .JoinAlias(() => joinedDossier.VERBRUIKSADRES,() => joinedVerbruiksAdres) 
    .JoinAlias(() => joinedAanvraag.EAN,() => joinedEan) 
    .JoinAlias(() => joinedEan.CtEnergietype,() => joinedEnergieType) 
    .JoinAlias(() => uitgaandebrief.BRIEFBESTEMMELINGENCollection,() => joinedBriefBestemmeling) 
    .JoinAlias(() => uitgaandebrief.CtBriefType,() => joinedBriefType) 
    .JoinAlias(() => uitgaandebrief.CtBriefStatus,() => joinedBriefStatus) 
    .JoinAlias(() => joinedBriefBestemmeling.CONTACTPERSOONCollection,() => joinedContactpersoon, JoinType.LeftOuterJoin) 
    .JoinAlias(() => joinedBriefBestemmeling.CtContactPersoonType,() => joinedBestemmelingType, JoinType.LeftOuterJoin) 
    .JoinAlias(() => joinedBriefBestemmeling.CtVerzendModus,() => joinedVerzendModus, JoinType.LeftOuterJoin) 
    .JoinAlias(() => joinedContactpersoon.CtContactpersoonType,() => joinedContactpersoonType, JoinType.LeftOuterJoin) 
    .JoinAlias(() => joinedContactpersoon.CtTaal,() => joinedContactpersoonTaal, JoinType.LeftOuterJoin) 
    .JoinAlias(() => joinedContactpersoon.TOEWIJZVERBRUIKVERANTCollection,() => joinedVerbruiksVerantw, JoinType.LeftOuterJoin) 
    .JoinAlias(() => joinedContactpersoon.CtProfielGroep,() => joinedProfielGroep, JoinType.LeftOuterJoin); 

Это только начало. Здесь идет часть фильтрации результатов (при необходимости).

if (briefType.HasValue) 
{ 
    baseQuery.Where(() => uitgaandebrief.BriefType == briefType.Value); 
} 

if (verzendModus.HasValue) 
{ 
    baseQuery.Where(() => joinedBriefBestemmeling.VerzendModus == verzendModus.Value); 
} 

if (!string.IsNullOrEmpty(binnenland) && binnenland.Trim() != "-1") 
{ 
    baseQuery.Where(() => joinedBriefBestemmeling.BinnenLand == binnenland.ToBoolean()); 
} 

Тогда я получил роль, чтобы выбрать материал, который мне нужно, и перевести его в DTO (NieuwePrintopdrachtenInfo).

NieuwePrintopdrachtenInfo nieuwePrintopdrachtInfo = null; 
baseQuery.SelectList(list => list 
    .Select(() => uitgaandebrief.UitgaandebriefId).WithAlias(() => nieuwePrintopdrachtInfo.UitgaandeBriefId) 
    .Select(() => uitgaandebrief.DatumInplanning).WithAlias(() => nieuwePrintopdrachtInfo.InplanningsDatum) 
    .Select(() => uitgaandebrief.ErrorReden).WithAlias(() => nieuwePrintopdrachtInfo.Probleem) 
    .Select(() => uitgaandebrief.ErrorNr).WithAlias(() => nieuwePrintopdrachtInfo.ErrorNummer) 
    .Select(() => uitgaandebrief.DatumCreatie).WithAlias(() => nieuwePrintopdrachtInfo.CreatieDatumBrief) 
    .Select(() => uitgaandebrief.DatumUpdate).WithAlias(() => nieuwePrintopdrachtInfo.DatumLaatsteWijzigingBrief) 
    .Select(() => uitgaandebrief.UserCreatie).WithAlias(() => nieuwePrintopdrachtInfo.BrieUserCreatie) 
    .Select(() => uitgaandebrief.UserUpdate).WithAlias(() => nieuwePrintopdrachtInfo.BriefUserUpdate) 
    .Select(() => uitgaandebrief.DatumAnnulatieElektriciteit).WithAlias(() => nieuwePrintopdrachtInfo.DatumElektriciteitGeannuleerd) 
    .Select(() => uitgaandebrief.DatumAnnulatieGas).WithAlias(() => nieuwePrintopdrachtInfo.DatumGasGeannuleerd) 
    .Select(() => joinedDossier.DossierId).WithAlias(() => nieuwePrintopdrachtInfo.DossierId) 
    .Select(() => joinedDossier.DossierNr).WithAlias(() => nieuwePrintopdrachtInfo.DossierNr) 
    .Select(() => joinedEnergieType.Omschrijving).WithAlias(() => nieuwePrintopdrachtInfo.EnergieTypeBrief) 
    .Select(() => joinedBriefType.Omschrijving).WithAlias(() => nieuwePrintopdrachtInfo.TypeBrief) 
    .Select(() => joinedVerzendModus.Omschrijving).WithAlias(() => nieuwePrintopdrachtInfo.VerzendModus) 
    .Select(() => joinedVerzendModus.Omschrijving).WithAlias(() => nieuwePrintopdrachtInfo.BestemmelingVerzendModus) 
    .Select(() => joinedBriefBestemmeling.BriefBestemmelingenId).WithAlias(() => nieuwePrintopdrachtInfo.BestemmelingId) 
    .Select(() => joinedBestemmelingType.Omschrijving).WithAlias(() => nieuwePrintopdrachtInfo.BestemmelingContactpersoonType) 
    .Select(() => joinedBriefBestemmeling.BestemmelingElektriciteit).WithAlias(() => nieuwePrintopdrachtInfo.BestemmelingElek) 
    .Select(() => joinedBriefBestemmeling.BestemmelingGas).WithAlias(() => nieuwePrintopdrachtInfo.BestemmelingGas) 
    .Select(() => joinedBriefBestemmeling.BinnenLand).WithAlias(() => nieuwePrintopdrachtInfo.BestemmelingBinnenLand) 
    .Select(() => joinedVerbruiksAdres.Land).WithAlias(() => nieuwePrintopdrachtInfo.LandVerbuiksadres) 
    .Select(Projections.Property(() => joinedContactpersoon.ContactpersoonId).As("BestemmelingContactPersoon.ContactPersoonId")) 
    .Select(Projections.Property(() => joinedContactpersoonType.Omschrijving).As("BestemmelingContactPersoon.TypeContactPersoon")) 
    .Select(Projections.Property(() => joinedContactpersoon.VoorNaam).As("BestemmelingContactPersoon.VoorNaam")) 
    .Select(Projections.Property(() => joinedContactpersoon.Naam).As("BestemmelingContactPersoon.Naam")) 
    .Select(Projections.Property(() => joinedContactpersoon.Straat).As("BestemmelingContactPersoon.Straat")) 
    .Select(Projections.Property(() => joinedContactpersoon.HuisNr).As("BestemmelingContactPersoon.HuisNummer")) 
    .Select(Projections.Property(() => joinedContactpersoon.BusNr).As("BestemmelingContactPersoon.BusNummer")) 
    .Select(Projections.Property(() => joinedContactpersoon.Gemeente).As("BestemmelingContactPersoon.Gemeente")) 
    .Select(Projections.Property(() => joinedContactpersoon.PostCode).As("BestemmelingContactPersoon.PostCode")) 
    .Select(Projections.Property(() => joinedContactpersoon.Appartement).As("BestemmelingContactPersoon.Appartement")) 
    .Select(Projections.Property(() => joinedContactpersoon.Verdieping).As("BestemmelingContactPersoon.Verdieping")) 
    .Select(Projections.Property(() => joinedContactpersoon.Telefoon1).As("BestemmelingContactPersoon.Telefoon1")) 
    .Select(Projections.Property(() => joinedContactpersoon.Telefoon2).As("BestemmelingContactPersoon.Telefoon2")) 
    .Select(Projections.Property(() => joinedContactpersoon.FAXNr).As("BestemmelingContactPersoon.Fax")) 
    .Select(Projections.Property(() => joinedContactpersoon.Email).As("BestemmelingContactPersoon.Email")) 
    .Select(Projections.Property(() => joinedContactpersoon.DatumCreatie).As("BestemmelingContactPersoon.DatumCreatie")) 
    .Select(Projections.Property(() => joinedContactpersoon.UserCreatie).As("BestemmelingContactPersoon.UserCreatie")) 
    .Select(Projections.Property(() => joinedContactpersoon.DatumUpdate).As("BestemmelingContactPersoon.DatumUpdate")) 
    .Select(Projections.Property(() => joinedContactpersoon.UserUpdate).As("BestemmelingContactPersoon.UserUpdate")) 
    .Select(Projections.Property(() => joinedContactpersoon.AdresBijTeWerken).As("BestemmelingContactPersoon.IsAdresBijTeWerken")) 
    .Select(Projections.Property(() => joinedContactpersoon.Titel).As("BestemmelingContactPersoon.Titel")) 
    .Select(Projections.Property(() => joinedContactpersoon.NietBesteldeBrief).As("BestemmelingContactPersoon.NietBesteldeBrief")) 
    .Select(Projections.Property(() => joinedContactpersoon.Land).As("BestemmelingContactPersoon.Land")) 
    .Select(Projections.Property(() => joinedContactpersoon.ContactpersoonAlsAanbrengerGebruikt).As("BestemmelingContactPersoon.ContactPersoonIdAlsAanbrenger")) 
    .Select(Projections.Property(() => joinedContactpersoon.ContactpersoonIsBetrokken).As("BestemmelingContactPersoon.ContactPersoonIsBetrokken")) 
    .Select(Projections.Property(() => joinedContactpersoon.NietAfgehaaldeBrief).As("BestemmelingContactPersoon.NietAfgehaaldeBrief")) 
    .Select(Projections.Property(() => joinedContactpersoonTaal.Omschrijving).As("BestemmelingContactPersoon.Taal")) 
    .Select(Projections.Property(() => joinedProfielGroep.Omschrijving).As("BestemmelingContactPersoon.IngegevenDoor")) 
    .Select(Projections.Property(() => joinedEan.Energietype).As("BestemmelingContactPersoon.EnergieType")) 
    .Select(Projections.Property(() => joinedVerbruiksVerantw.ToewijzigingVerbruiksVerantwoordelijkeId).As("BestemmelingContactPersoon.VerbruiksVerantwoordelijkeId"))); 

Да, я знаю, что это беспорядок. Теперь, когда вы сделали это так, вы с радостью узнаете, что мы почти там. Это код, который я использую для возврата результатов (это является общим и использует DeepTransform, который я нашел here)

protected IEnumerable<TR> GetDeepTransformedPagedList<T, TR>(IQueryOver<T, T> query) where TR : class 
{ 
    PagingSettings.Count = query.Clone().Select(Projections.CountDistinct(PagingSettings.PropertyNameToCountOn)).FutureValue<int>().Value; 
    query = query.TransformUsing(new DeepTransformer<TR>()); 

    if (PagingSettings.Enabled) 
    { 
     var pagedQuery = query.Skip(GetPagingStartRowIndex()).Take(PagingSettings.PageSize); 
     return pagedQuery.List<TR>(); 
    } 

    return query.List<TR>(); 
} 

EDIT

После полезный пост Радим Кёлера я узнал что группа не поможет мне с моей проблемой. Вот почему я объясню настоящую проблему. В коде предыдущий запрос строится и расширяется с помощью Пропуска & Взять для подкачки. В моей ситуации я получаю 50 результатов при выполнении запроса. Эти 50 результатов содержат дубликаты и должны быть сгруппированы по UitgaandeBriefId. Вот почему оригинальные разработчики написали этот код, который выполняется после возвращения результатов из БД.

ILookup<int, IEnumerable<NieuwePrintopdrachtenInfo>> groupedbrieven = 
      (from tbInfo in brieven 
      group tbInfo by tbInfo.UitgaandeBriefId into g 
      let UitgaandeBriefId = g.Key 
      let Group = g as IEnumerable<NieuwePrintopdrachtenInfo> 
      select new { UitgaandeBriefId, Group }) 
      .ToLookup(result => result.UitgaandeBriefId, result => result.Group); 

Этот код по-прежнему работает, но приводит к получению только 32 результатов. Это приводит к тому, что мои страницы никогда не содержат 50 результатов. Первоначальный разработчик использовал подкачку на стороне сервера вместо того, чтобы делать это в БД, поэтому у него никогда не было этой проблемы (производительность была мудрой, это была огромная проблема). Вот почему я реорганизовал его так, чтобы он выполнялся намного быстрее, но это приводит к тому, что не получается получить 50 результатов. Я предполагаю, что мне нужно добавить четкий текст, но я не знаю, как это сделать, чтобы работать в NHibernate, так как я привык работать с EntityFramework.

ответ

3

В общем, если мы хотим изменить нашу проекцию, чтобы быть с помощью GROUP BY, мы должны изменить все «SELECT» части, чтобы быть либо частью GROUP BY или SUM, MIN ...

Мы может сделать это с такой синтаксис

// firstly 
// the original part from the question above 
baseQuery.SelectList(list => list 
    ... 
    .Select(() => joinedBriefBestemmeling.BinnenLand) 
     .WithAlias(() => nieuwePrintopdrachtInfo.BestemmelingBinnenLand) 
    .Select(() => joinedVerbruiksAdres.Land) 
     .WithAlias(() => nieuwePrintopdrachtInfo.LandVerbuiksadres) 
    .Select(Projections.Property(() => joinedContactpersoon.ContactpersoonId) 
     .As("BestemmelingContactPersoon.ContactPersoonId")) 
    .Select(Projections.Property(() => joinedContactpersoonType.Omschrijving) 
     .As("BestemmelingContactPersoon.TypeContactPersoon")) 
    ... 



// changed, to use GROUP BY 
baseQuery.SelectList(list => list 
    ... 
    .SelectGroup(() => joinedBriefBestemmeling.BinnenLand) 
     .WithAlias(() => nieuwePrintopdrachtInfo.BestemmelingBinnenLand) 
    .SelectGroup(() => joinedVerbruiksAdres.Land) 
     .WithAlias(() => nieuwePrintopdrachtInfo.LandVerbuiksadres) 
    .Select 
     (Projections.Alias 
     (Projections.GroupProperty 
      (Projections.Property(() => joinedContactpersoon.ContactpersoonId)) 
     , "BestemmelingContactPersoon.ContactPersoonId")) 
    .Select 
     (Projections.Alias 
     (Projections.GroupProperty 
      (Projections.Property(() => joinedContactpersoonType.Omschrijving)) 
     , "BestemmelingContactPersoon.TypeContactPersoon")) 
    ... 

Итак, теперь мы имеем GROUP BY (а не просто SELECT) замены исходного кода. Но мы можем сделать больше, мы можем ввести эти (только быструю версию) методов расширения (только свет версия, на самом деле - но работает)

public static class Extensions 
{ 
    public static NHibernate.Criterion.Lambda.QueryOverProjectionBuilder<T> GroupByProperty<T>(
     this NHibernate.Criterion.Lambda.QueryOverProjectionBuilder<T> builder, 
     System.Linq.Expressions.Expression<Func<object>> propertyExpression, 
     System.Linq.Expressions.Expression<Func<object>> aliasExpression) 
    { 
     var alias = aliasExpression.ParseProperty(); 

     var propertyProjection = Projections.Property(propertyExpression); 
     var groupProjection = Projections.GroupProperty(propertyProjection); 
     var withAliasProjection = Projections.Alias(groupProjection, alias); 

     builder.Select(withAliasProjection); 
     return builder; 
    } 

    public static string ParseProperty<TFunc>(this System.Linq.Expressions.Expression<TFunc> expression) 
    { 
     var body = expression.Body as System.Linq.Expressions.MemberExpression; 
     if (body.IsNull()) 
     { 
      return null; 
     } 

     string propertyName = body.Member.Name; 

     ParseParentProperty(body.Expression as System.Linq.Expressions.MemberExpression, ref propertyName); 

     // change the alias.ReferenceName.PropertyName 
     // to just   ReferenceName.PropertyName 
     var justAPropertyChain = propertyName.Substring(propertyName.IndexOf('.') + 1); 
     return justAPropertyChain; 
    } 

    static void ParseParentProperty(System.Linq.Expressions.MemberExpression expression, ref string propertyName) 
    { 
     if (expression.IsNull()) 
     { 
      return; 
     } 
     // Parent.PropertyName 
     propertyName = expression.Member.Name + "." + propertyName; 

     ParseParentProperty(expression.Expression as System.Linq.Expressions.MemberExpression, ref propertyName); 
    } 
} 

И приведенный выше код может быть более читаемым и общий, без волшебной строки

baseQuery.SelectList(list => list 
    ... 
    .GroupByProperty(() => joinedBriefBestemmeling.BinnenLand) 
     ,() => nieuwePrintopdrachtInfo.BestemmelingBinnenLand) 
    .GroupByProperty(() => joinedVerbruiksAdres.Land) 
     ,() => nieuwePrintopdrachtInfo.LandVerbuiksadres) 
    .GroupByProperty(() => joinedContactpersoon.ContactpersoonId) 
     .() => nieuwePrintopdrachtInfo.BestemmelingContactPersoon.ContactPersoonId) 
    .GroupByProperty(() => joinedContactpersoonType.Omschrijving) 
     .() => nieuwePrintopdrachtInfo.BestemmelingContactPersoon.TypeContactPersoon) 
    ... 

ПРИМЕЧАНИЕ IsNull() также расширение

+0

Спасибо за ваш ответ. Это очень помогло мне. Но, похоже, группировка на все не дает мне результата, которого я хочу. Использование SelectGroup работает, но, похоже, не исправляет мою ситуацию. Я обновлю свой вопрос, чтобы объяснить это. – Beejee

+0

Также я попробовал ваш метод расширения. Это выглядит лучше, чем то, что я использовал. Проблема в том, что теперь я получаю NullReferenceExceptions. Как обычно, ошибка ocurs в той части, где выполняется запрос, поэтому поиск реальной проблемы - это боль в ... Я начал отладку метода ParseProperty, и похоже, что «тело» фактически имеет нулевое значение. Я что-то упускаю? – Beejee

+0

K выглядит так, как будто я смог его исправить. Я думаю, что ваша версия чересчур чередована. Я добавил некоторые дополнительные материалы, чтобы проверить «NodeType», и когда это «ExpressionType.Convert», я использую это как свое тело »((UnaryExpression) expression.Body) .Operand as MemberExpression« Теперь работает как шарм. Теперь мне нужно только вернуть правильные результаты – Beejee