NHibernate LINQ: NullReferenceException in select clause (compiled lambdna)

439 views
Skip to first unread message

triad...@gmail.com

unread,
May 5, 2015, 12:40:09 PM5/5/15
to nhibernate-...@googlegroups.com
Consider the following domain model:

public class Person {
    public virtual int Id { get; set;}
 
    public virtual string Name { get;set; }
 
    public virtual Pet Pet { get; set; }
}

public class Pet {
    public virtual int Id { get; set;}
 
    public virtual string Name { get;set; }
}


And the following LINQ query:

from Person p in session.Query<Person>()
select new {
    Person = p.Name,
    Pet = p.Pet.Name
}


This query executes fine, until there is a person which has no pet. The query then fails with a NullReferenceException somewhere deep in NHibernate (or - at least it appears to be). This is because NHibernate simply compiles the projection expression and executes it. To mitigate this one would need to write the query as follows:

from Person p in session.Query<Person>()
select new {
    Person = p.Name,
    Pet = p.Pet != null ? p.Pet.Name : null
}

The behavior is in my opinion confusing because other ORM (Entity Framework, amongst other) use database semantics here and this would simply cause the Expression "p.Pet.Name" to return NULL if any of the properties references in the expression return null. Even if one might want to adhere to the LINQ to Objects semantics instead, the NullReferenceException is best caught then rethrown with some useful message. The other problem is that writing these null checks polute the Expression tree passed to the LINQ provider.

I propose to do make the following change:

Before the Expression is compiled, a visitor will rewrite the Expression tree to add null checks. When a property is null default(T) will be returned. ExpressionToHqlTranslationResults seems the best place to do this.

Are you willing to receive a contribution that will implement this, or is the current behavior deliberate?

Gunnar Liljas

unread,
May 6, 2015, 6:11:45 AM5/6/15
to nhibernate-development
I'm wondering in what kind of scenario you are experiencing this, because that is most definitely not how NHibernate works. I use queries like the mentioned all the time, without issues.

Do you have some code you could show?

Rewriting the expression to include null checks would create a more complex Linq tree, a more complex HQL tree and eventually a more complex SQL expression.

/G

--

---
You received this message because you are subscribed to the Google Groups "nhibernate-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-develo...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

triad...@gmail.com

unread,
May 6, 2015, 1:46:48 PM5/6/15
to nhibernate-...@googlegroups.com
Well in my post, that's the code.

I currently work around it by actually writing these null checks myself but at the database level they make no sense of course.

Try the code with these data in the tables:
Person
Id Name PetId
1 Test 1

Pet
Id Name
1 Test

Notice how the query just executes fine. Now add a second record but with PetId NULL. Notice the crash.

Note I was not proposing to do the transformation of the Expression Tree before it is handed to HQL but before it is compiled to a delete for use in the ResultTransformer.
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-development+unsub...@googlegroups.com.

Jeffrey Becker

unread,
May 6, 2015, 2:20:01 PM5/6/15
to nhibernate-...@googlegroups.com
How will C# 6.0's null-conditional operator affect your proposal? that is in C# 6.0 you can write:

from Person p in session.Query<Person>()
select new {
    Person = p.Name,
    Pet = p.?Pet.Name
}

rather than

from Person p in session.Query<Person>()
select new {
    Person = p.Name,
    Pet = p.Pet != null ? p.Pet.Name : null
}
And the compiler will spit out an equivalent expression tree.

triad...@gmail.com

unread,
May 6, 2015, 2:42:49 PM5/6/15
to nhibernate-...@googlegroups.com
That wouldn't really solve the problem would it? You'd still polute the expression tree with null checks, thereby complicating the underlying HQL and SQL.

Also, this wouldn't help anyone who cannot take advantage of the new C# compiler yet.

Oskar Berggren

unread,
May 6, 2015, 2:49:20 PM5/6/15
to nhibernate-...@googlegroups.com
2015-05-06 18:42 GMT+01:00 <triad...@gmail.com>:
Well in my post, that's the code.


Mappings etc? Can you wrap it up as a self-contained unit test?
 

I have a bunch of code that looks similar. This is the example I could find quickly.

attribute => new AttributeValueRecord
            {
                ConnectionId = attribute.DBConnection.Id,


Where DBConnection is actually NULL for most records in the database, and it works fine.

Judging from the names of your parameters, one difference is that in the example I could find quickly, I'm only accessing the primary key of the referenced object. You can check if that makes a difference (won't solve your issue, of course, but would be interesting to know).

Another thing is are you really sure that the exception is generated from inside the compiled expression? Or might it be somewhere else?

What NHibernate version is this?

/Oskar


triad...@gmail.com

unread,
May 8, 2015, 11:37:47 AM5/8/15
to nhibernate-...@googlegroups.com
Find the example attached. Happens at least with NHibernate 4.0.3GA and apparently also with current master.
NH9999.zip

Gunnar Liljas

unread,
May 8, 2015, 11:59:58 AM5/8/15
to nhibernate-development
The problem in your code is that you're projecting the integer ID into a non nullable ID on the other side. If you make it nullable it will work. The error message is not very nice, admittedly, but it's correct behavior that is fails. Falling back to default(T) would result in misrepresentative data.

/G

--

---
You received this message because you are subscribed to the Google Groups "nhibernate-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-develo...@googlegroups.com.

triad...@gmail.com

unread,
May 8, 2015, 2:38:37 PM5/8/15
to nhibernate-...@googlegroups.com
Yes, you're right. Are you willing to accept an pull request with this test and an change that allows a nice error message to be thrown instead?
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-development+unsub...@googlegroups.com.

Gunnar Liljas

unread,
May 8, 2015, 4:26:53 PM5/8/15
to nhibernate-...@googlegroups.com
If "you" means me, I'm not the one to decide, but generally every catchable error that can be better explained is most welcome. I'd certainly vote for such an improvement.

/g


Sent from Mailbox


To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-develo...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--

---
You received this message because you are subscribed to the Google Groups "nhibernate-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhibernate-develo...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages