views:

42

answers:

1

I'm writing a WPF NHibernate Desktop App using Session Per Presenter. I have a list view showing all the saved SalesOrders and an Edit Sales Order form when you double click on a Sales Order.

Each of these forms has a Session Object which lasts for the lifetime of the form. When a SalesOrder is saved it publishes an Event which tells the list view to re-load. The EditForm is definitely saving to the database and the ListView is definitely selecting from the database. However, the session that belongs to the ListViewPresenter is not updating its entities with those retrieved from the database. It just returns the same values as when the listSession was first loaded before anything was saved.

Below is some code which best replicates the scenario:-

        [Test]
    public void SessionPerPresenter()
    {
        //This session is the one that is used to load all salesorders from the      database. It's lifetime is the lifetime of the form but as you double click on an entry in the list to edit it will stay alive longer than the session in the edit form
        ISession listSession = NHibernateHelper.OpenSession();

        SalesOrder order = new SalesOrder("P123435", "ACME");
        order.AddLine(new SalesOrderLine("Beans", 15));
        order.AddLine(new SalesOrderLine("Coke", 24));
        order.AddLine(new SalesOrderLine("Pepsi", 3));
        order.AddLine(new SalesOrderLine("Apples", 4));

        //this session is the equivalent of the one in the Edit Form as soon as the entity is Saved
        //the session is disposed
        using (ISession session = NHibernateHelper.OpenSession())
        {
            session.SaveOrUpdate(order);
            ID = order.SalesOrderID;
        }

        //retrieve all SalesOrders from the database and store them in a list
        IList<SalesOrder> salesOrders = listSession.CreateCriteria<SalesOrder>().List<SalesOrder>();
        foreach (SalesOrder so in salesOrders)
        {
            Console.WriteLine(so.ToString());
        }

        //edit the selected order and update its order code value and resave
        using (ISession session = NHibernateHelper.OpenSession())
        {
            hydratedSalesOrder = session.Get<SalesOrder>(ID);
            hydratedSalesOrder.OrderCode = "1234-5678";
            session.SaveOrUpdate(hydratedSalesOrder);
            session.Flush();
        }

        //re-retrieve the list of orders from the database. Using SQLServer Profiler / NHibernate profiler
        //you can see the query being sent to the database so I don't believe it is in the cache. Indeed, if you run
        //the query directly against the database the value 1234-5678 is returned. Can't work out why
        //the listSession does not have the values read from the database in it but has the values from the
        //original list retrieval.
        salesOrders = listSession.CreateCriteria<SalesOrder>().List<SalesOrder>();
        foreach (SalesOrder so in salesOrders)
        {
            Console.WriteLine(so.ToString());
        }
        listSession.Close()
    }

Can someone help me with what is going on here? What am I doing wrong? Am I missing something vital? If it didn't query the database I would think it was something to do with the first level cache but that seems unlikely.

A: 

On way to ensure that your entities are not cached is to clear the session with ISession.Clear(). Also you can evict individual entities by calling ISession.Evict(object entity).

If you not sure of what is happening in your application, consider a profiling tool such as nhprof.

Quick note: using a session for the lifetime of a dialog can be handy in small applications with no concurrency problems, but you will get in trouble on the long run. A session should be opened late, and closed early.

Pierre 303
The reason for Session Per Form is that I thought that it was recommended best practice for a Desktop App as per Building a Desktop To-Do Application with NHibernate(msdn.microsoft.com/en-us/magazine/ee819139.aspx). I have used NHibernate profiler and it is definitely calling the database but does not seem to reload the listSession. With two forms open you can only Evict the entity that was edited in the form you are currently in. It does not reload entities that were modified in the other form from db. To me, using Clear() seems like you are losing a lot of the benefits of NHibernate.
LostInWPF
Session Per Form will work in a lot of case. I do that when I need quick results. Regarding your comment, that's probably the cache then. Don't use Clear() everywhere of course, use it when you need it. Without more information about your problem, that's the only advice I can give you. Sorry for that.
Pierre 303