Jamie Ide
Jamie Ide

Reputation: 49251

Criteria API With clause in many-to-many relationship

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

Answers (2)

Jamie Ide
Jamie Ide

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

Mark Perry
Mark Perry

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

Related Questions