Reputation: 49251
NHibernate 3 introduced the With clause for the Criteria API. I'm trying to use it to restrict the join on a many-to-many relationship and NHibernate is applying the additional join restriction to the linking table rather than the correct table.
I have a many-to-many relationship between Player and Address and I want to return a Player and its mailing address by restricting Address.IsMailingAddress. Restricting IsMailingAddress in the where clause does not return records that do not have a mailing address (or no address at all), so I need to restrict it in the join. The code below is simplified and I can't use HQL.
The query:
var target = session.CreateCriteria<Player>()
.SetProjection(Projections.Property("PlayerId"))
.CreateAlias("Addresses", "ad", JoinType.LeftOuterJoin, Restrictions.Eq("ad.IsMailingAddress", true))
.Add(Restrictions.Eq("LastName", "Anonymous"))
.List();
generates the SQL:
SELECT this_.PlayerId as y0_
FROM dbo.VPlayerExisting this_
left outer join dbo.LinkPlayAddr addresses3_
on this_.PlayerId = addresses3_.PlayerId
and (ad1_.MailingAddressFlag = 1 /* @p0 */)
left outer join dbo.VAddress ad1_
on addresses3_.AddressId = ad1_.AddressId
and (ad1_.MailingAddressFlag = 'Anonymous' /* @p1 */)
WHERE this_.Name_Last = @p2
MailingAddressFlag (the column that's mapped to IsMailingAddress) is restricted in the many-to-many linking table where it doesn't exist and the LastName restriction is applied in the join to the Address table. The query does get issued but of course the database server raises an exception.
Is this a bug, unsupported or am I doing it wrong?
Upvotes: 0
Views: 1717
Reputation: 49251
The solution is below. There should be just one address with IsMailingAddress set, so the or condition allows the join to succeed if there is one or none using an inner join.
var target = session.CreateCriteria<Player>()
.SetProjection(Projections.Property("PlayerId"))
.CreateAlias("Addresses", "ad", JoinType.InnerJoin)
.Add(RestrictionsOr(Restrictions.Eq("ad.IsMailingAddress", true), Restrictions.IsNull("ad.AddressId")))
.Add(Restrictions.Eq("LastName", "Anonymous"))
.List();
Upvotes: 0
Reputation: 1725
var target = session.CreateCriteria<Player>()
.CreateAlias("Addresses", "ad", JoinType.InnerJoin)
.Add(Restrictions.Eq("LastName", "Anonymous"))
.Add(Restrictions.Eq("ad.IsMailingAddress",true))
.List();
Just inner join to mailing address with the restriction on IsMailingAddress?
Should bring back Players (where LastName is "Anonymous") with their MailingAddress where they have an Address and that address is flagged as IsMailingAddress.
Upvotes: 1