3

I use FluentNhibernate and I see NHibernate performing many queries when references of associations are mapped with NotFound.Ignore().

Since the referential integrity of my legacy database is kinda crappy, I'd like to know if there's a workaround or if there's an alternative mapping I can use.

Example:

//no query when loading the entity
References<User>(x => x.User, "UserId").LazyLoad().Nullable();

//performs a hundred queries when I load my entities
References<User>(x => x.User, "UserId").LazyLoad().Nullable().NotFound.Ignore();
Falcon
  • 3,150
  • 2
  • 24
  • 35
  • This seems to be a bug in NHibernate: http://216.121.112.228/browse/NH-1001 – Falcon Feb 25 '11 at 13:02
  • 6
    Not just any bug. A bug that's been open/ignored for 4 years! This is why I have zero confidence in NHibernate these days. People are more than happy to put new features in, but not fix bugs that don't effect them. – Danny Tuppeny Feb 25 '11 at 13:08
  • Updated link on the bug: https://nhibernate.jira.com/browse/NH-1001 ... and sadly, it's still open after 6 years – saluce Jul 30 '13 at 19:55

1 Answers1

6

This is a known problem unfortunately, there is an issue in NHibernate JIRA (https://nhibernate.jira.com/browse/NH-1001)

There is a workaround though but it isn't pretty. In the Entity you need to do something along the lines of this:

class Entity {

    private int? _userId;

    private User user;

    public User User 
    {
        get { 
            if (_userId == null)
                return null;

            return user;                
        };
        set {
            if (value == null)
                _userId = null;
            else
                _userId = value.UserId;

            _user = value;
        };
    }
 }

And in the mapping you would map the reference as normal but without the not-found = ignore setting but you also map the foreign key field:

 References<User>(Reveal.Membmer<User>("_user"), "UserId").LazyLoad();
 Map(Reveal.Membmer<int?>("_userId")).Nullable().Not.Update().Not.Insert(); // Suppress updates and inserts so that they don't conflict with the mapping of User.

Basically you let NHibernate operate as normal on the _user field and then use the _userId field to manually do the null check. This way you avoid the N+1 selects problem. The downside is that it complicates the Entity and will make queries harder to write. If you want to be able to use the User-property in a LINQ query for example you will have to expose the internal _user field and use that instead.

saluce
  • 13,035
  • 3
  • 50
  • 67
Samuel Otter
  • 608
  • 3
  • 7
  • While I appreciate your efforts, I don't really want to touch my persistance-ignorant entities because of this NHibernate bug. I guess it's easier for us to enforce referential integrity then, it has to be done anyway. Thanks for your help, though. – Falcon Feb 25 '11 at 13:07
  • Thanks for this - we are unable to force RI, because of legacy DB, I'll give this a try and let you know how it works out! – Stuart.Sklinar Apr 19 '12 at 11:03
  • After some trying, this actually put us back to where we were at the beginning, and does not resolve our issue, we still have a null assoc and a N+1 issue. – Stuart.Sklinar Apr 19 '12 at 13:50
  • Are you sure the N+1 issue is caused by the bug I mentioned and not something else? Because my solution is simply a workaround to that problem. NHibernate will issue extra selects for null values immediately after the query even when the property is never accessed when not-found=ignore is set and this solution avoids it simply by not using not-found=ignore. I haven't tested this in recent versions of NHibernate though so maybe it issues extra selects even without not-found=ignore (I used it with v2.1 I think). – Samuel Otter Apr 19 '12 at 16:20