Entries from December 2007 ↓

NHibernate and the SqlTypeException

NHibernate is a wonderful piece of technology, I love it probably more than is reasonable for code. It does however, occasionally scare you with some seemingly odd behavior. I say seemingly, because every time I’ve had trouble it’s actually ended up being my own fault. This is one of those times.

Picture a simple page, with a DeleGrid control, being bound using NHiberate. Baring in mind how the DeleGrid works, two queries were being executed, one to return the first page of data and another to get the total row-count for the grid. These queries were identical apart from the paging in one, and the projection in the other.

Upon execution of the second query, NHibernate was throwing a SqlTypeException for a SqlDateTime overflow. SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM. This was pretty bizarre. Why on earth would the first query succeed (and bring back records, fully populated), but the same query again would die.

A good place to start for NHibernate debugging is always the logs, so I delved in. I discovered NHibernate was attempting to execute an update statement just before it tried the second query. It just kept getting stranger, why would a straightforward query cause an update?

I thought i’d investigate why the update statement was failing first, then I’d tackle the problem of why it was even updating at all. Looking at the query I identified the column that was causing the exception, it was (as expected) a DateTime column that was trying to be set to DateTime.MinValue. This exception is thrown because .Net and SQL Server have different ideas over what the minimum value for a DateTime should be.

Now, why would this column be being set at all? Well, it ends up that the column in the database was nullable, but the property in the object wasn’t. So because DateTime is a value type and cannot be set to null, NHibernate was populating it with the closest value to null as it could manage.

That was the key, as soon as I had that realisation, it was obvious what the problem was.

NHibernate knew that the database had a nullable column, but it had to manage with the non-nullable field on the object. When it came to run the second query, it noticed that the property wasn’t null as the mapping file said it should be, so it determined the value must have changed. It then attempted to persist those changes before executing the query!

To break it down

  1. Nullable column pulled into a non-nullable field forces NHibernate to create the smallest value it can.
  2. NHibernate then checks for any changes, expecting a null on that field but finding a value.
  3. Object now considered dirty because value has allegedly changed.
  4. NHibernate performs an update before it pulls back the data agian.

So the fix was simply to make the DateTime in the object a DateTime?, a nullable DateTime. That got rid of the false update, and fixed my queries. Simple when you know what the problem is.

So the moral of the story is: Make sure everything is in sync - schema, mappings and POCOs.

Percieved value and developer education

A comment on a post at Jeremy D. Miller’s blog caught my eye. To paraphrase, Jeff Tucker says that part of the problem of the lack of perceived value of ORM/TDD/IOC etc is down to the developers in question not having experienced the reason these tools exist.

He makes a fair point really, we use tools because they solve a problem for us, if we haven’t actually experienced what they’re solving then their value is appears to be much less than what it is. Similarly, the value of such tools and methodologies is nil when there is a lack of recognition of a problem even existing.

However, I don’t think the solution is to have developers learn “the hard way”. To discover the problem before using the solution is a problem in its-self. If we were to all learn from our own mistakes, and not from each-others, then we’re not going to make much progress as we’re all going to be solving the same problems.

Most people will have struggled with whatever problem has necessitated the creation of these tools/methodologies, so I think it’s more a case of helping the developers connect the dots; “You remember such and such a problem? Well that’s easily solved using methodology/technology x!”, rather than making them retrace the steps of every developer before them.

On testing implementation

I’ve found my-self in the situation of retro-fitting a library of code with unit tests, not a good situation to be in. However, what’s more concerning is I’ve just caught my-self writing tests that are heavily testing the implementation of a method; rather than simply testing if the method does what it’s supposed to.

There are a few problems with falling into this trap. Firstly, it’s very brittle. Secondly, you shouldn’t be concerned with the internals. Thirdly, it’s very time consuming.

To elaborate…

It’s brittle because you’re essentially writing a script of how the method is going to execute, which of course will change whenever you do any refactoring. So your tests break every time you make a change to your code, which is not only annoying, but will quickly lead to test cancer, where tests aren’t run or are commented out.

You shouldn’t be concerned with the internals, because as long as your method is doing as requested, you shouldn’t really care how it’s achieving it’s goal. Not bolting down the internals allows methods to be refactored without too much resistance from the tests. This will increase the signal-to-noise ratio, allowing failing tests to be representative of a problem greater than your basic refactorings.

Finally, it’s time consuming simply because you’re duplicating most of your work. All the time you spent writing the method (or the test, if done first) is then duplicated writing the tests (or code…). This is a pain, because as mentioned above, you’ll keep doing this work every time you change the method.

The hardest part is learning how to not do this kind thing blindly. There are plenty of times when you’ll need this kind of testing, but don’t make it your default! Test expectations, not implementations.

DeleGrid - a paged GridView control

Introducing the DeleGrid

The DeleGrid is a control derived from the ASP.Net GridView, that delegates its data retrieval back out of the control. This allows the developer full control over the records that are retrieved, thus allowing proper paging to be implemented using whatever collection type you prefer.

Why the DeleGrid?

It came about because I wanted a nice way of implementing paging using NHibernate without having the grid know about it. I really didn’t want NHibernate to leave my data layer, so I needed a nice way of the grid calling my DAL with the paging parameters.

I didn’t want to utilise the ObjectDataSource because honestly, it made me feel dirty. I’m all for delegation and composition, but not when it means creating a control in my HTML that acts as a DAL. Additionally, I didn’t feel the ObjectDataSource was very type-safe, or refactor-friendly, with the method names and types exposed in the HTML. Granted, the IDE would probably pick it up, but I don’t want to risk a runtime failure on it.

Using the DeleGrid (aka The Example)

After getting the source or assembly and doing the usual song-and-dance, add a reference to the control to your page:

<%@ Register Assembly="JAGregory.Controls.DeleGrid"
  Namespace="JAGregory.Controls" TagPrefix="jag" %>

Then create an instance of the control, turning the paging on and setting the correct page size:

<jag:DeleGrid ID="grid" runat="server" AllowPaging="true"
  PageSize="4" />

Now you have a control set up, however it sill won’t bind correctly. So, you need to attach the event handlers in the code-behind.

protected override void OnInit(EventArgs eventArgs)
{
  base.OnInit(eventArgs);

  grid.TotalRecordCountRequest += delegate {
    // code to get total
  };
}

Starting with the TotalRecordCountRequest, this event is raised when the grid needs to know how many records in total your grid is going to be displaying. This number is the cumulative count of all the pages. I’m going to use a simple repository pattern to factor away my DAL logic.

The OnInit method is now:

protected override void OnInit(EventArgs eventArgs)
{
  base.OnInit(eventArgs);

  ProductRepository repos = new ProductRepository();

  grid.TotalRecordCountRequest += delegate {
    return repos.GetTotal();
  };
}

Now your grid knows how many records it has overall, however we still haven’t told it how to actually get the data. So now to put the code in the PageDataRequest handler. This event is raised when the grid is needing a new page of data, this will get called once on initial data-bind, then again every time you change the page (or sorting etc…).

The OnInit method is now:

protected override void OnInit(EventArgs eventArgs)
{
  base.OnInit(eventArgs);

  ProductRepository repos = new ProductRepository();

  grid.TotalRecordCountRequest += delegate {
    return repos.GetTotal();
  };
  grid.PageDataRequest += delegate(object sender, DataRequestEventArgs e) {
    return repos.GetRange(e.Start, e.Size);
  };
}

The event-handler receives an instance of DataRequestEventArgs, which contains the start index of the current page of data, and the number of records in a page. It also contains a SortField and SortDirection, for when sorting is enabled on the grid; however, we aren’t utilising them in this example.

Finally we just need to bind the grid on page load. We don’t re-bind the grid on post-back, due to that being handled internally in the DeleGrid.

protected override void OnLoad(EventArgs eventArgs)
{
  base.OnLoad(eventArgs);

  if (!IsPostBack)
    grid.DataBind();
}

That’s all there is to it!

You don’t need to use delegates, the normal event-handler syntax is fine (and probably preferred for larger examples). I just did it this way for brevity’s sake.

Further reading…

Testing

I’ve written a small number of tests that cover the implementation of the grid as best I can. There was only so-far I was willing to go to test the control, as it’s heavily tied to the ASP.Net implementation; which can get pretty messy for testing without using something like NUnitASP, which was a bit much for one control.. I’ve got coverage of about 85% of the code, which I’d say is pretty reasonable anyway.

Sorting

As mentioned above, you can implement sorting in your handlers by accessing the SortField and SortDirection properties of the event arguments.

DeleGrid.AlwaysRequestTotal

By default the DeleGrid only requests the total number of records on the initial data-bind, however if you see this as being a problem (such as with rapidly changing data-sets), you may want to set this property to true so it refreshes the total on every data-bind.

Downloads

The DeleGrid is open-source under the new BSD License; read the license for what you’re allowed to do.

You can download the source here: Download Source.
You can download the latest binary here: Download Binary.

The source is also accessible from Subversion at: http://jagregory.googlecode.com/svn/trunk/DeleGrid/ (using user jagregory-read-only)

All patches are welcomed with open arms!

kick it on DotNetKicks.com