Showing posts with label nHibernate. Show all posts
Showing posts with label nHibernate. Show all posts

Thursday, March 19, 2009

nHibernates poor validation of mapping files

One thing that I have come to notice as I've been using nHibernate is that it does a very poor job at validating that mapping files make sense. One could argue that I shouldn't write poor shoddy mapping files in the first place. I would beg instead to tell me when I've written a poor shoddy mapping file.

Here's the scenario I just faced. I was working with an Entity which has a One to One relationship with some legacy code which doesn't use nHibernate. When I first mapped this I had an identity column on the table. After thinking about it, I want to enforce that we have a one to one so I then modified my schema and mapping file to use the FK field as its identity. I went from this:



class name="MyOptions"  lazy="false">


    <id column="id" name="Id"  >


        <generator class="native"/>


    </id>


    <property name="FkId"/>


</class>





To this:



<class name="MyOptions"  lazy="false">


    <id column="FkId" name="FkId" unsaved-value="any">


        <generator class="assigned"/>


    </id>


    <property name="FkId"/>


</class>




I fire this up, and we have no initialization issues, I am also to read data in without an issue. But when I go to save the data, I get an IndexOutOfRange exception when nHibernate tries to access a parameter.

Do you see the issue? I left my property for FkId in the mapping file. I needed to remove it since it is now the Id. Which caused parts of nHibernate to break. Interestingly enough when I look at the insert statement it only had the FkId field being inserted into once. So part of nHibernate handles my shoddy mapping file but other parts do not.

Anyways nHibernate is still an awsome tool, and hats off to the people who devote their time without pay to such a great tool.

Thursday, November 20, 2008

S#arp Arch & MSTest

I have just started a new project, and as such I took this chance to further my exploration into alternative models then standard ASP.Net Webforms. I have been curious to learn more about ASP.Net MVC, and I wanted to continue exploring nHibernate.

When I first implemented nHibermate I used a model which was basically based upon Billy McCafferty's work; however, during this I found severe limitations one of which I blogged about here. Billy has been hard at work at developing a new set of patterns which are encapsulated in an architecure called S#arp.

One small glitch I ran into related to my use of MSTest. Billy has created a base class that all Respository test classes can inherit from. This class will setup and tear down nHibernate session and roll back any transactions which should help keep your database at a known state.

However, this class assumes the use of nUnit. Which when you try and use this from within MSTest it causes a null reference exception when you try and access the repository. If your like me and want to ue MSTest a real simple solution is this:

  1. Inherit from the RepositoryUnitTestsBase class.
  2. Explitly call the Setup and Tear down methods

So your class should basically look like:


    [TestClass]
    public class ProductRepositoryTest : RepositoryUnitTestsBase
    {
        [TestInitialize()]
        public void MyTestInitialize()
        {
            this.SetUp();
        }
 
        [TestCleanup()]
        public void MyTestCleanup()
        {
            this.TearDown();
        }
    }


I'd love to see these methods moved out into a seperate utility type class.

Enjoy
-Josh

Monday, October 27, 2008

nHibernate Transaction & Session Mgmt

I've been using nHibernate for a couple of months now, and have been using Billy McCafferty's basic architecture with some of my own added spin. Today I noticed a strange behavior in nHibernate 2.0 which I've not fully explained or explored so bare with me.

I have an Organization object, and there's a screen where a user can modify the properties and save the object back to the database. I wanted to add additional error handling around the app to make it friendlier, so I removed one of my safeguards that let me generate an error (passing in a string larger then what the DB is configured for), and ran through my test. Essentially the page method doing the save looked like:

 protected void saveOrgInfo_Click(object sender, EventArgs e)
    {
        try
        {
            DoSave();
            this.saveStatus.Text = "Your changes have been saved.";
            this.saveStatus.Visible = true;
            this.saveStatus.CssClass = "successText";
        }
        catch (Exception ex)
        {
            CutterLogEntry.LogError("Error saving OrgInfo", ex);
 
            this.saveStatus.Text = "There was an error saving your changes. Please try again.";
            this.saveStatus.Visible = true;
            this.saveStatus.CssClass = "warnText";
        }
    }
 
    private void DoSave()
    {
        NHibernateSessionManager.Instance.BeginTransactionOn();
        MembershipService memService = new MembershipService();
        this.orgInfo.UpdateOrganizationInfo(Util.CurrentUser.Organization);
        memService.SaveOrganization(Util.CurrentUser.Organization);
        NHibernateSessionManager.Instance.CommitTransactionOn();
    }


One of my modifications was to add overloads to his methods which don't accept a path to the config file, and when this is used it defaults to the Web.Config / App.Config file.

What I found interesting was that I was being redirected to an error page, when based on this code my catch should have handled this. The call stack in the error page indicated that the error was originating in my httpModule which tries to close any open session.

When we tried to call Close on the session the nHibernateSessionManager also calls a flush() which will cause nHibernate to try and save the Organization entity again. The interesting thing here is if we look at the logic in the Commit method, when it fails the SessionManager tries to rollback the transaction:


        public void CommitTransactionOn(string sessionFactoryConfigPath) {
            ITransaction transaction = (ITransaction)ContextTransactions[sessionFactoryConfigPath];
 
            try {
                if (HasOpenTransactionOn(sessionFactoryConfigPath)) {
                    transaction.Commit();
                    ContextTransactions.Remove(sessionFactoryConfigPath);
                }
            }
            catch (HibernateException) {
                RollbackTransactionOn(sessionFactoryConfigPath);
                throw;
            }
        }



If we follow the source the Rollback will call the rollback transaction and then call the CloseSessionOn() method. The CloseSessionOn() method will attempt to flush and then close the session. The call to flush will generate an exception here. Because of this exception, the close method is never called.

So when the HttpModule kicks off it sees there is an active session, and then trying to close it calls a flush which generates the exception again. All in all this one exception was thrown three times so far.

I find it strange that nHibernate would try and save an object that was "Saved" within a transactional context. I would imagine that a Rollback should clear out all dirty entities; however, it doesn't. The quick fix is as I said to modify the Rollback method so that it closes (but not flush).

Enjoy

Josh