NHibernate QueryOver with Left Self Join
I have a table of positions where a position can have a related position (but not necessarily), and every position has a last modified date. I then want to fetch all positions (of a given type) that was modified between two given dates (i.e. either the "main" position or the related position was modified). In SQL I would do this as follows:
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')
Now I'm fairly new to NHibernate (3.0) and QueryOver, and I have a bit of a problem translating this SQL query to C# code. I have read some examples and tried looking at other questions, but sadly no luck.
My initial attempt was something like this:
public IList<ShipPosition> GetModifiedShipPositions(IList<ShipPositionType> positionTypes, DateTime modifiedFrom, DateTime modifiedTo)
{
var result = Session.QueryOver<ShipPosition>()
.WhereRestrictionOn(p => p.ShipPositionType).IsInG(positionTypes)
.And(Restrictions.Or(
Restrictions.Where<ShipPosition>(p => p.ModifiedDate.IsBetween(modifiedFrom).And(modifiedTo)),
Restrictions.Where<ShipPosition>(p => p.RelatedShipPosition != null
&& p.RelatedShipPosition.ModifiedDat开发者_如何转开发e.IsBetween(modifiedFrom).And(modifiedTo))));
return result.List();
}
But this throws a KeyNotFoundException (The given key was not present in the dictionary). I have tried experimenting with JoinQueryOver
and JoinAlias
as I suspect it's one of those that are missing, but I haven't managed to get it right.
If anybody could point me in the right direction (or to a question where this is already answered), I would be very grateful!
Update:
I tried writing the query using linq:
var query = Session.Query<ShipPosition>().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();
That didn't throw any exceptions, but I didn't get the desired result (missing one case there p.RelatedShipPosition is null.
And just to have mentioned that, using HQL works fine and gives the same result as the SQL query:
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<ShipPosition>();
So the question still remains: how can I translate this into using QueryOver?
Update 2:
Just in case it is of interest to anybody, I'll include what my final code looked like, after help from MonkeyCoder's answer:public IList<ShipPosition> GetModifiedShipPositions(DateTime modifiedFrom, DateTime modifiedTo, params ShipPositionType[] positionTypes)
{
ShipPosition relatedShipPosition = null;
var result = Session.QueryOver<ShipPosition>()
.Left.JoinAlias(sp => sp.RelatedShipPosition, () => relatedShipPosition)
.WhereRestrictionOn(sp => sp.ShipPositionType).IsInG(positionTypes)
.And(Restrictions.Or(
Restrictions.Where<ShipPosition>(sp => sp.ModifiedDate.IsBetween(modifiedFrom).And(modifiedTo)),
Restrictions.Where(() => relatedShipPosition.ModifiedDate.IsBetween(modifiedFrom).And(modifiedTo))));
return result.List();
}
I don't know if you've already tried this one out - I'm unable to test this properly - as I'm away from my computer, but I though that you could give a try:
ShipPosition shipPosition = null;
ShipPositionType shipPositionType = null;
RelatedShipPosition relatedShipPosition = null;
var result = QueryOver.Of<ShipPosition>(() => 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));
I hope it helps!
精彩评论