Ras
Ras

Reputation: 628

Nhibernate query with null child

I need to retrieve all the users with a valid Wish property (so not null). This is the xml of my class:

<class name="Project.Engine.Domain.User,Project.Engine" table="Users" lazy="true">
  <id name="UserID" column="UserID">
    <generator class="native" />
  </id>
  <property name="Firstname" column="Firstname" type="string" not-null="true"
    length="255" />
  <property name="Lastname" column="Lastname" type="string" not-null="true"
    length="255" />
  <property name="Email" column="Email" type="string" not-null="true"
    length="255" />
  <one-to-one name="Wish" cascade="all" property-ref="UserID"
    class="Project.Engine.Domain.Wish, Project.Engine"  />
</class>

The method to get all my users is the following:

public PagedList<User> GetAll(int pageIndex, int pageSize,
    string orderBy, string orderByAscOrDesc)
{
    using (ISession session = NHibernateHelper.OpenSession())
    {
        var users = session.CreateCriteria(typeof(User));
        users.Add(Restrictions.IsNotNull("Wish"));
        return users.PagedList<User>(session, pageIndex, pageSize);
    }
}

As you can notice, I have added the Restriction on the child object. This doesn't work properly as the method return all users including the ones with Wish property as null. Any help?

this is the xml for child:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class name="Project.Engine.Domain.Wish,Project.Engine" table="Wish" lazy="false">
    <id name="WishID" column="WishID">
      <generator class="native" />
    </id>
    <property name="UserID" column="UserID" type="int" not-null="true" length="32" />
    <property name="ContentText" column="ContentText" type="string" not-null="false" length="500" />
    <property name="Views" column="Views" type="int" not-null="true" length="32" />
    <property name="DateEntry" column="DateEntry" type="datetime" not-null="true" />
  </class>
</hibernate-mapping>   

Upvotes: 1

Views: 713

Answers (2)

Fr&#233;d&#233;ric
Fr&#233;d&#233;ric

Reputation: 9854

one-to-one mapping using property-ref is not an "actual" one-to-one, and usually this is a sign a many-to-one mapping should be used instead.
Maybe this is not related to your trouble, but you may give it a try.

An "actual" one-to-one has the dependent table primary key equals to the parent table primary key. (Dependent table, Wish in your case, would have a foreign primary key, UserId in your case. See this example.)

I have sometime "played" with 'one-to-one property-ref', and I always ended giving it up due to many issues. I replaced that with more classical mappings, either changing my db for having an actual one-to-one, or using a many-to-one and living with a collection on child side though it would always contain a single element.

Upvotes: 0

Fr&#233;d&#233;ric
Fr&#233;d&#233;ric

Reputation: 9854

Well, there is a bug with one-to-one and null testing of the side which may not exist. I had already encountered it but forgot about it. The property-ref just render it a bit more tricky to diagnose, but it does exist on actual one-to-one too.

Here is its corresponding issue in NHibernate tracking tool.

Workaround: test for null state of an non-nullable property of Wish, like Wish.Views.

Forgive the wild guess on test syntax, I do not use anymore since years, but try by example:

public PagedList<User> GetAll(int pageIndex, int pageSize,
    string orderBy, string orderByAscOrDesc)
{
    using (ISession session = NHibernateHelper.OpenSession())
    {
        var users = session.CreateCriteria(typeof(User));
        users.Add(Restrictions.IsNotNull("Wish.Views"));
        return users.PagedList<User>(session, pageIndex, pageSize);
    }
}

Using , I confirm this workaround works with my own projects, which gives by example:

// The "TotalAmount != null" seems to never be able to come false from a 
// .Net run-time view, but converted to SQL, yes it can, if TransactionRecord
// does not exist.
// Beware, we may try "o.TransactionsRecord != null", but you would get struck
// by https://nhibernate.jira.com/browse/NH-3117 bug.
return q.Where(o => o.TransactionsRecord.TotalAmount != null);

I maintain my other answer since you may consider using a many-to-one instead, especially since you do not have made a bidirectionnal mapping (no corresponding constrained one-to-one in Wish) in addition to not having an actual one-to-one. many-to-one does not suffer of the bug.

Upvotes: 1

Related Questions