Nhibernate Group By 和 Alias To Bean

Nhibernate Group By and Alias To Bean

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

我想查询数据库 (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());
}

然后我得到了 select 我需要的部分并将其翻译成 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")));

是的,我知道这是一团糟。既然你已经做到了这一步,你会很高兴知道我们快到了。这是我用来 return 结果的代码(它是通用的,使用我发现的 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>();
}

编辑

在 Radim Köhler 的帮助 post 之后,我发现分组不能帮助我解决我的问题。这就是为什么我要解释真正的问题。 在代码中,先前的查询是使用 Skip & Take 构建和扩展的,用于分页目的。在我的情况下,我在执行查询时得到 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.

一般来说,如果我们想将投影更改为使用 GROUP BY,我们必须将 所有 "SELECT" 部分更改为 GROUP BY 的一部分或总和,最小值 ...

我们可以用这种语法来完成

// 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() 也是扩展