Nhibernate Group By和Alias To Bean

我一直在努力将旧的查询翻译成Nhibernate。 我们正在将旧项目从Nhibernate 2升级到最新版本。 我使用QueryOver语法,因为Linq不是一个选项,因为查询的复杂性(同事的建议)。

我想查询DB(Oracle)以获得一些必须分组的结果。 结果我需要一个我的DTO的分组集合。 我也注意到nhibernate在转换为具有复杂属性的DTO时遇到了麻烦(嵌套DTO)为了解决这个问题,我找到了这个主题。 这很好用,但我不是魔术弦的粉丝……

我将添加一些代码片段,了解我的怪物查询在这一刻的表现。 问题是我似乎无法弄清楚如何在不破坏其他所有内容的情况下添加组。 所以我想在一个属性上分组但在我的结果中仍然有DTO。 就像是:

ILookup<int,IEnumerable> 

欢迎任何帮助。

抱歉变量和类是荷兰语-_-

 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)

 protected IEnumerable GetDeepTransformedPagedList(IQueryOver query) where TR : class { PagingSettings.Count = query.Clone().Select(Projections.CountDistinct(PagingSettings.PropertyNameToCountOn)).FutureValue().Value; query = query.TransformUsing(new DeepTransformer()); if (PagingSettings.Enabled) { var pagedQuery = query.Skip(GetPagingStartRowIndex()).Take(PagingSettings.PageSize); return pagedQuery.List(); } return query.List(); } 

编辑

在RadimKöhler的有用post之后我发现一个小组将不会帮助我解决我的问题。 这就是为什么我会解释真正的问题。 在代码中,之前的查询是构建的,并通过Skip&Take进行扩展以进行分页。 在我的情况下,我在执行查询时得到50个结果。 这50个结果包含重复项,需要按UitgaandeBriefId进行分组。 这就是原始开发人员编写此代码的原因,该代码在结果从数据库返回后执行。

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

此代码仍然有效,但只能获得32个结果。 这会导致我的页面永远不会包含50个结果。 原始开发人员使用服务器端分页而不是在数据库上执行它,因此他从未遇到过这个问题(性能明智这是一个巨大的问题)。 这就是为什么我重构它以便它可以更快地执行,但这导致不能获得50个结果。 我想我需要添加一个独特的但我不知道如何让它在NHibernate中工作,因为我习惯使用EntityFramework。

通常,如果我们想要将我们的投影更改为使用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 GroupByProperty( this NHibernate.Criterion.Lambda.QueryOverProjectionBuilder builder, System.Linq.Expressions.Expression> propertyExpression, System.Linq.Expressions.Expression> 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(this System.Linq.Expressions.Expression 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()也是扩展名