views:

525

answers:

1

I'm using NHibernate + Fluent to handle my database, and I've got a problem querying for data which references other data. My simple question is: Do I need to define some "BelongsTo" etc in the mappings, or is it sufficient to define references on one side (see mapping sample below)? If so - how? If not please keep reading.. Have a look at this simplified example - starting with two model classes:

public class Foo
{
    private IList<Bar> _bars = new List<Bar>();

    public int Id { get; set; }
    public string Name { get; set; }
    public IList<Bar> Bars
    {
        get { return _bars; }
        set { _bars = value; }
    }
}

public class Bar
{
    public int Id { get; set; }
    public string Name { get; set; }
}

I have created mappings for these classes. This is really where I'm wondering whether I got it right. Do I need to define a binding back to Foo from Bar ("BelongsTo" etc), or is one way sufficient? Or do I need to define the relation from Foo to Bar in the model class too, etc? Here are the mappings:

public class FooMapping : ClassMap<Foo>
{
    public FooMapping()
    {
        Not.LazyLoad();
        Id(c => c.Id).GeneratedBy.HiLo("1");
        Map(c => c.Name).Not.Nullable().Length(100);
        HasMany(x => x.Bars).Cascade.All();
    }
}

public class BarMapping : ClassMap<Bar>
{
    public BarMapping()
    {
        Not.LazyLoad();
        Id(c => c.Id).GeneratedBy.HiLo("1");
        Map(c => c.Name).Not.Nullable().Length(100);
    }
}

And I have a function for querying for Foo's, like follows:

public IList<Foo> SearchForFoos(string name)
{
    using (var session = _sessionFactory.OpenSession())
    {
        using (var tx= session.BeginTransaction())
        {
            var result = session.CreateQuery("from Foo where Name=:name").SetString("name", name).List<Foo>();
            tx.Commit();
            return result;
        }
    }        
}

Now, this is where it fails. The return from this function initially looks all fine, with the result found and all. But there is a problem - the list of Bar's has the following exception shown in debugger:

base {NHibernate.HibernateException} = {"Initializing[MyNamespace.Foo#14]-failed to lazily initialize a collection of role: MyNamespace.Foo.Bars, no session or session was closed"}

What went wrong? I'm not using lazy loading, so how could there be something wrong in the lazy loading? Shouldn't the Bar's be loaded together with the Foo's? What's interesting to me is that in the generate query it doesn't ask for Bar's:

select foo0_.Id as Id4_, foo0_.Name as Name4_ from "Foo" foo0_ where foo0_.Name=@p0;@p0 = 'one'

What's even more odd to me is that if I'm debugging the code - stepping through each line - then I don't get the error. My theory is that it somehow gets time to check for Bar's during the same session cause things are moving slower, but I dunno.. Do I need to tell it to fetch the Bar's too - explicitly? I've tried various solutions now, but it feels like I'm missing something basic here.

+7  A: 

This is a typical problem. Using NHibernate or Fluent-NHibernate, every class you use that maps to your data is decorated (which is why they need to be virtual) with a lot of stuff. This happens all at runtime.

Your code clearly shows an opening and closing of a session in a using statement. When in debugging, the debugger is so nice (or not) to keep the session open after the end of the using statement (the clean-up code is called after you stop stepping through). When in running mode (not stepping through), your session is correctly closed.

The session is vital in NH. When you are passing on information (the result set) the session must still be open. A normal programming pattern with NH is to open a session at the beginning of the request and close it at the end (with asp.net) or keep it open for a longer period.

To fix your code, either move the open/close session to a singleton or to a wrapper which can take care of that. Or move the open/close session to the calling code (but in a while this gets messy). To fix this generally, several patterns exist. You can look up this NHibernate Best Practices article which covers it all.

EDIT: Taken to another extreme: the S#arp architecture (download) takes care of these best practices and many other NH issues for you, totally obscuring the NH intricacies for the end-user/programmer. It has a bit of a steep learning curve (includes MVC etc) but once you get the hang of it... you cannot do without anymore. Not sure if it is easily mixed with FluentNH though.

Using FluentNH and a simple Dao wrapper

See comments for why I added this extra "chapter". Here's an example of a very simple, but reusable and expandable, Dao wrapper for your DAL classes. I assume you have setup your FluentNH configuration and your typical POCO's and relations.

The following wrapper is what I use for simple projects. It uses some of the patterns described above, but obviously not all to keep it simple. This method is also usable with other ORM's in case you'd wonder. The idea is to create singleton for the session, but still keep the ability to close the session (to save resources) and not worry about having to reopen. I left the code out for closing the session, but that'll be only a couple of lines. The idea is as follows:

// the thread-safe singleton
public sealed class SessionManager
{
    ISession session;
    SessionManager()
    {
        ISessionFactory factory = Setup.CreateSessionFactory();
        session = factory.OpenSession();
    }

    internal ISession GetSession()
    {
        return session;
    }

    public static SessionManager Instance
    {
        get
        {
            return Nested.instance;
        }
    }

    class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested()
        {
        }

        internal static readonly SessionManager instance = new SessionManager();
    }
}


// the generic Dao that works with your POCO's
public class Dao<T>
    where T : class
{
    ISession m_session = null;

    private ISession Session
    {
        get
        {
            // lazy init, only create when needed
            return m_session ?? (m_session = SessionManager.Instance.GetSession());
        }
    }

    public Dao() { }

    // retrieve by Id
    public T Get(int Id)
    {
        return Session.Get<T>(Id);
    }

    // get all of your POCO type T
    public IList<T> GetAll(int[] Ids)
    {
        return Session.CreateCriteria<T>().
            Add(Expression.In("Id", Ids)).
            List<T>();
    }

    // save your POCO changes
    public T Save(T entity)
    {
        using (var tran = Session.BeginTransaction())
        {
            Session.SaveOrUpdate(entity);
            tran.Commit();
            Session.Refresh(entity);
            return entity;
        }
    }

    public void Delete(T entity)
    {
        using (var tran = Session.BeginTransaction())
        {
            Session.Delete(entity);
            tran.Commit();
        }
    }

    // if you have caching enabled, but want to ignore it
    public IList<T> ListUncached()
    {
        return Session.CreateCriteria<T>()
            .SetCacheMode(CacheMode.Ignore)
            .SetCacheable(false)
            .List<T>();
    }

    // etc, like:
    public T Renew(T entity);
    public T GetByName(T entity, string name);
    public T GetByCriteria(T entity, ICriteria criteria);

Then, in your calling code, it looks something like this:

Dao<Foo> daoFoo = new Dao<Foo>();
Foo newFoo = new Foo();
newFoo.Name = "Johnson";
daoFoo.Save(newFoo);         // if no session, it creates it here (lazy init)

// or:
Dao<Bar> barDao = new Dao<Bar>();
List<Bar> allBars = barDao.GetAll();

Pretty simple, isn't it? The advancement to this idea is to create specific Dao's for each POCO which inherit from the above general Dao class and use an accessor class to get them. That makes it easier to add tasks that are specific for each POCO and that's basically what NH Best Practices was about (in a nutshell, because I left out interfaces, inheritance relations and static vs dynamic tables).

Abel
+1 Thanks for a good answer. Will need to process this, and I'll get back to you.
stiank81
Who gave this -1? And why? What do you disagree on?
stiank81
Might've been an NH-disliker/hater? If people downvote, I always hope they're so kind to explain why, so I can improve my post and remove any errors.
Abel
Ok - obviously the session closing is the problem. Made the session singleton and removed my using-clauses. This made the problem go away, but I can't say the solution makes me happy.. Handling session from calling code doesn't sound like a good solution. And I'm not sure how you'd solve this with a wrapper? I'm picturing a wrapper being used from or around the function which does the query - but this would still close the session when the function finishes, and hence not solve my problem? Will look into the Best Practices link.
stiank81
Btw; note that I'm not using asp.net. Using WPF and WCF - the issue is on the server side where the database is handled. So I guess S#arp isn't of relevance, but the best practices link is probably still relevant though it seem to be written for asp.net?
stiank81
I remember a thorough discussion about session management, I'll try to locate it. The Best Practices are also relevant for non-asp.net applications, but it's a tough read and worth several visits when you improve with NH. A typical solution (provided there) is to create a DAO layer. For each class you create an empty DAO class with a static GetXXXDao on a main DAO class, which triggers the singleton on first use and removes the burden on the calling code. This is a creational and factory pattern (if you're familiar with GoF). I'll see if I can create a FluentNH typical example for you.
Abel
Thanks for the update! (up'ed some of your other great answers too for being very helpful!). This looks very nice and clean. But do I really want the singleton? I would guess the idea is that the dao object's lifetime should depend on the calling function - which creates the dao. Causing the session to live long enough, but die with the dao. But if the Nested and instance is static - will there actually be only one session? When should I dispose the session anyway?
stiank81
Please feel free to check out my new question specifically on session lifetime: http://stackoverflow.com/questions/2013467/what-should-be-the-lifetime-of-an-nhibernate-session
stiank81