3 Stimmen

NHibernate QueryOver mit Left Self Join

Ich habe eine Tabelle von Positionen, wobei eine Position eine verwandte Position haben kann (aber nicht unbedingt), und jede Position hat ein letztes Änderungsdatum. Ich möchte dann alle Positionen (eines bestimmten Typs) abrufen, die zwischen zwei bestimmten Daten geändert wurden (d. H. entweder die "Haupt" -Position oder die verwandte Position wurde geändert). In SQL würde ich das wie folgt tun:

SELECT * FROM ShipPosition sp
LEFT JOIN ShipPosition sp2 ON sp.RelatedShipPositionID = sp2.ShipPositionID
WHERE sp.ShipPositionTypeID IN (11,12)
AND (sp.ModifiedDate BETWEEN '2011-09-09 08:00' AND '2011-09-09 12:00'
     OR sp2.ModifiedDate BETWEEN '2011-09-09 08:00' AND '2011-09-09 12:00')

Jetzt bin ich ziemlich neu bei NHibernate (3.0) und QueryOver, und ich habe ein kleines Problem, diese SQL-Abfrage in C#-Code zu übersetzen. Ich habe einige Beispiele gelesen und versucht, andere Fragen anzusehen, aber leider kein Glück.

Mein erster Versuch sah ungefähr so aus:

public IList GetModifiedShipPositions(IList positionTypes, DateTime modifiedFrom, DateTime modifiedTo)
{

    var result = Session.QueryOver()
        .WhereRestrictionOn(p => p.ShipPositionType).IsInG(positionTypes)
        .And(Restrictions.Or(
            Restrictions.Where(p => p.ModifiedDate.IsBetween(modifiedFrom).And(modifiedTo)),
            Restrictions.Where(p => p.RelatedShipPosition != null
                                               && p.RelatedShipPosition.ModifiedDate.IsBetween(modifiedFrom).And(modifiedTo))));
    return result.List();
}

Aber das wirft eine KeyNotFoundException (Der angegebene Schlüssel war nicht im Wörterbuch vorhanden). Ich habe versucht, mit JoinQueryOver und JoinAlias zu experimentieren, da ich vermute, dass eines davon fehlt, aber ich habe es nicht geschafft, es richtig zu machen.

Wenn jemand mich in die richtige Richtung weisen könnte (oder zu einer Frage, in der dies bereits beantwortet wurde), wäre ich sehr dankbar!

Update:

Ich habe versucht, die Abfrage mit Linq zu schreiben:

var query = Session.Query().Where(p
    => positionTypes.Contains(p.ShipPositionType)
    && ((p.ModifiedDate > modifiedFrom && p.ModifiedDate < modifiedTo)
    || (p.RelatedShipPosition != null && p.RelatedShipPosition.ModifiedDate > modifiedFrom && p.RelatedShipPosition.ModifiedDate < modifiedTo)));
return query.ToList();

Das hat keine Ausnahmen ausgelöst, aber ich habe nicht das gewünschte Ergebnis erhalten (ein Fall fehlt, in dem p.RelatedShipPosition null ist.

Und nur um das erwähnt zu haben, mit HQL funktioniert einwandfrei und liefert das gleiche Ergebnis wie die SQL-Abfrage:

var queryString = @"
        SELECT shipPosition
        FROM ShipPosition shipPosition
        LEFT JOIN shipPosition.ShipPositionType shipPositionType
        LEFT JOIN shipPosition.RelatedShipPosition relatedShipPosition
        WHERE shipPositionType.SystemName IN (:positionTypes)
        AND (shipPosition.ModifiedDate BETWEEN :modifiedFrom AND :modifiedTo
            OR relatedShipPosition.ModifiedDate BETWEEN :modifiedFrom AND :modifiedTo)";

var query = Session.CreateQuery(queryString);
query.SetParameterList("positionTypes", positionTypes.Select(pt => pt.SystemName).ToArray());
query.SetParameter("modifiedFrom", modifiedFrom);
query.SetParameter("modifiedTo", modifiedTo);

return query.List();

Die Frage bleibt also bestehen: Wie kann ich dies in QueryOver übersetzen?

Update 2:
Nur für den Fall, dass es jemanden interessiert, werde ich zeigen, wie mein endgültiger Code nach Hilfe aus der Antwort von MonkeyCoder aussah:

public IList GetModifiedShipPositions(DateTime modifiedFrom, DateTime modifiedTo, params ShipPositionType[] positionTypes)
{
    ShipPosition relatedShipPosition = null;

    var result = Session.QueryOver()
        .Left.JoinAlias(sp => sp.RelatedShipPosition, () => relatedShipPosition)
        .WhereRestrictionOn(sp => sp.ShipPositionType).IsInG(positionTypes)
        .And(Restrictions.Or(
            Restrictions.Where(sp => sp.ModifiedDate.IsBetween(modifiedFrom).And(modifiedTo)),
            Restrictions.Where(() => relatedShipPosition.ModifiedDate.IsBetween(modifiedFrom).And(modifiedTo))));

    return result.List();
}

2voto

MonkeyCoder Punkte 2580

Ich weiß nicht, ob du das schon ausprobiert hast - Ich kann dies nicht richtig testen - da ich nicht an meinem Computer bin, aber ich dachte, dass du es versuchen könntest:

ShipPosition shipPosition = null;
ShipPositionType shipPositionType = null;
RelatedShipPosition relatedShipPosition = null;

var result = QueryOver.Of(() => shipPosition)
    .JoinAlias(() => shipPosition.ShipPositionType, () => shipPositionType)
    .JoinAlias(() => shipPosition.RelatedShipPosition, () => relatedShipPosition)
    .WhereRestrictionOn(() => relatedShipPosition.SystemName).IsInG(positionTypes)
    .And(Restrictions.Or(
        Restrictions.Where(() => shipPosition.ModifiedDate).IsBetween(modifiedFrom).And(modifiedTo)),
        Restrictions.Where(() => relatedShipPosition.ModifiedDate).IsBetween(modifiedFrom).And(modifiedTo));

Ich hoffe, es hilft!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X