For whatever sadistic reason, I signed up to give not one, but two, presentations at the upcoming Portland Code Camp. I have also made a commitment to Chris Bilson to come up to Tacoma to present on nHibernate in support of the Lunch Time Speakers Group. So in an effort to kill two birds with one stone, I sat down today and whipped up a outline of what I intend to present. Your thoughts, comments, suggestions & ridicule are welcome.

Introduction to ORM with nHibernate

What is ORM?

  • Object/Relational Impedance Mismatch
  • Pros
  • Cons

The Basics

  • What Do I Need To Get Started?
  • Mapping Part I: Class Map XML
  • Configuration Part I: How Do I Connect to My Database?
  • nHibernate Config file
  • web.config or app.config
  • ISession
  • ISessionFactory
  • Configuration
  • ITransaction
  • Unit of Work Pattern

A Better Way

  • Mapping Part II: ClassMap<T> For the Win!
  • Mapping Part III: AutoMapping Using Conventions
  • Overriding for Special Cases
  • Configuration Part II: The Fluent Configuration API

Querying

  • Hibernate Query Language (HQL): Just Say No
  • Criteria API
  • The New Hotness: Linq to nHibernate

Tricks of the Trade

  • Generating Schema  From The Object Model
  • Updating Schema When The Objects Change
  • Integration Tests That Validate Your Mappings
  • Using SQLLite To Make BDD Style Tests Lightening Fast

Resources

  • nhforge.org
  • fluentnhibernate.org
  • summerofnhibernate.com
  • nhibernate in action from Manning

 
Categories:

Earlier today, I blogged about a Kobayashi Maru situation I found myself in with the code base which must not be named. I was stuck making the choice between completely destabilizing our code base at the end of a sprint while trying to track down all the possible side effects of fixing the root cause of my problem or putting yet another little hack into our code.

Over lunch, I was describing the situation to Terry, one of our other developers. We came to the conclusion that we needed to go with the hack method to meet our obligations to the Scrum Master, but we should get the root cause of the issue in the backlog so we could resolve it later.

But this means that our hack would be in place in a fairly unobvious place and in 6 months when we get the item into the sprint backlog, we might fix the issue but forget to remove the hack.

Typically this is solved by adding a "TODO:" in the comments right by your hack to remind you to remove it in the future. Our code base is littered with them.

ToDoWTF

Notice the one from 2005. :-( And these are only the comments that happen to have "TODO:" in them. Obviously, we need a better way to remind ourselves to go back and take care of housekeeping tasks when we change the system.

Then Terry said, "Wouldn't it be awesome if we had a test that would fail if this condition changes?" and the light went off. We talked about it some more and as soon as I got back to the office, I created a new utility project called TestTools.HackToDos. I then wrote two specification style tests.

The first test demonstrates the low level call that causes the issue:

[TestClass]
public class When_Cleaning_A_DataTable_With_Null_Dates : GridViewHacksContext
{
protected override void BecauseOf()
{
Tools.Instance().CleanData(dataWithNulls);
}

[TestMethod]
public void The_Nulls_Will_Be_Converted_To_Invalid_Dates()
{
dataWithNulls.Rows[0].Field(0).ShouldEqual(DateTime.MinValue);
}
}

The second test demonstrates what I would like to happen:

[TestClass]
public class When_Binding_GridView_To_DataView_With_Invalid_Dates : GridViewHacksContext
{
protected override void BecauseOf()
{
Tools.Instance().CleanData(dataWithNulls);
grid.BindData(dataWithNulls.DefaultView, layoutManager.Columns);
}

[TestMethod]
public void The_Invaid_Date_Should_Be_Hacked_Out()
{
Assert.IsTrue(String.IsNullOrEmpty(grid.Rows[0].Cells[0].Text));
}
}

Running these tests resulted in the first test passing and the second failing. I then introduced my "hack fix" as a part of my extension methods to GridView:

private static void hackOutBadDates(GridView grid)
{
foreach (var row in grid.GridDataRows())
{
foreach (TableCell cell in row.Cells)
{
if (cell.Text.Contains(@"01/01/0001"))
cell.Text = null;
}
}
}

Now both tests pass. And my lovely jQuery awesome grid now displays null dates correctly.

yagniYAY

Now I can safely check this code in and feel like I performed my boy scout duty today without feeling dirty. In 6 months when someone on the team gets the sprint item to fix our goofy handling of null values in data tables, my first test is either going to fail or even fail to compile. That dev will go look at the test and find himself staring at this comment block:

/// 
/// TODO: this spec demonstrates a low level flaw in our dataaccess layer where
/// DateTime columns in a DataTable are set to a value of DateTime.MinValue
/// after every read. When we attempt to bind a Grid View to the resulting DataTable
/// null dates are displayed as "01/01/0001" which DateTime.Parse throws as invalid.
/// I have added a hack to our grid view extension methods that fixes this problem.
/// If these tests fails, it might be because we fixed the low level issue and
/// the hack should be removed along with these tests.
///

And there you have ToDos that are enforced by unit tests.


 
Categories: Development | Unit Testing

YAGNI, "you aren't gonna need it" (should maybe have a sub-principal of "will prolly come back around to bite you in the ass later"), is a principal that gets tossed around a lot to keep gold plating developers like me in check. I just love traveling down bunny trails adding "wouldn't it be cool if..." features to the code I am working on. I recognize this this fault in myself and routinely stop to ask myself, "why am I doing this?" or "how does this relate to the task at hand?" maybe even a "what effects could this have in other areas of the system?" on occasions where I am feeling extra saucy.

I wish someone had asked those questions of the person who wrote this code that I discovered deep in the bowls of "the code base that must not be named".

public void CleanData(DataRow row, int upperLimit)
{
if ((row == null))
{
return;
}

//Look at each field in this row
for (int i = 0; i <= upperLimit; i++)
{
//If this field is null, change the value of the field to some type of empty value
if ((row.ItemArray.GetValue(i)) == DBNull.Value)
{
// Handle all possible types of data in a datatable
switch (row.Table.Columns[i].DataType.Name)
{
case "Boolean":
row[i] = false;
break;

case "Byte":
row[i] = (byte) 0;
break;

case "Byte[]":
row[i] = 0;
break;

case "DateTime":
row[i] = DateTime.MinValue;
break;

case "Decimal":
row[i] = (decimal) 0;
break;

case "Double":
row[i] = (double) 0;
break;

case "Int16":
case "Int32":
case "Int64":
row[i] = Conversions.ToInteger(0);
break;

case "Long":
row[i] = Conversions.ToLong(0);
break;

case "Short":
row[i] = (short) 0;
break;

case "Single":
row[i] = (float) 0;
break;

case "String":
row[i] = "";
break;

//If a type shows up that is not handled, throw a meaningful exception
default:
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
Resources.InvalidConversionDataType,
row.Table.Columns[i].DataType.Name));
}
}
 

Now at first glance, this doesn't seem that bad. A very nice example of gold plating. I can hear the developer now saying to him or herself, "wouldn't it be cool if we never had to check for DBNull? Seriously that would be jalapeno hot!"

I wonder if the developer was thinking about this possible unintended consequence that I am currently working to resolve.

yagnidoh

So, now I have nonsense dates being rendered out to my page. Trying to address this issue at the source, I commented out the line that sets dates to a min value in the clean data method to see what happens.

yagniuberdoh

Well that is interesting. To make it perfectly obvious _StaffMember is a hashtable. And the value of it's Staff_Lockout_Datetime key is and empty string. And this error is being caused by me switching a low level data clean up method to not change a null value to a invalid date.

So how is this hashtable created? Once again, deep in the bowls of the code base is a method called ToHashtable that takes a datarow and returns a hashtable. The interesting block of code is below.

 

For i As Integer = 0 To tableRow.Table.Columns.Count - 1

If tableRow.IsNull(i) Then

'Convert nulls to 0 or "" before putting in hashtable
Select Case tableRow.Table.Columns(i).DataType.Name

Case "Int64", "Int32", "Int16", "Decimal", "Double", "Single"

'Numeric data types
If convertDBNullIntToZero Then
ht.Add(tableRow.Table.Columns(i).ColumnName, 0)
Else
ht.Add(tableRow.Table.Columns(i).ColumnName, "")
End If

Case Else

'String, DateTime data types
ht.Add(tableRow.Table.Columns(i).ColumnName, "")

End Select

Else
'blah blah blah
End If
Next

 

So once again we have some gold plating here attempting to prevent other developers from having to check for null values. The interesting thing is why are we checking for nulls on a row that has already been though the clean process when the dataset was retrieved from the database? It seems to me this block of code has never been hit before today. Maybe our gold plate-ers are not comparing notes on all the filigree they are adorning our objects with.

The icing to this cake is the line of code where it all blows up. Look at it. What are they really trying to do?

If CDate(_StaffMember("Staff_Lockout_Datetime")) <> CDate(Nothing) Then

All they really want to know is if the hash table value is null. Exactly what our gold plate-ers have so desperately been trying to hide from us.

So I am left with two choices:

  1. Attempt to locate all the areas in the entire application where this gold plating has caused assumptions and correct them to let null dates make their way out the UI and hope like hell I don't introduce more bizarre side effects.
  2. Come up with some goofy databinding or prerender hack to work around the invalid dates.

Both options leave a bad taste in my mouth and a strong desire to drown my sorrows with a nice Irish whiskey.


 
Categories: Development | Fundamentals

This just might be the most awesome JavaScript I have written in the better part of a decade.

function sortEnd() {
$('thead th', this).each(function() {

var $className = $(this).attr('class');
var $anchor = $('a', this);
var titleText;

switch ($className) {
case "headerSortUp":
titleText = $anchor.text() + ', sorted acending';
break;
case "headerSortDown":
titleText = $anchor.text() + ', sorted decending';
break;
default:
titleText = $anchor.text() + ', sort by';
}

$anchor.attr('title', titleText);
$anchor.attr('alt', titleText);

});
};

 
Categories: Development

Just in case you missed it the Portland Code Camp is on for May 30th, 2009 at Reed College. It looks to be a great time already and is so far totally dominated by the ALT.NET movement.

Seattle's Justin Bozonier has posted a sessions on Artistic Expression Through Code and Solid Principles in Practice and TDD/BDD. Chris Bilson posted sessions on Client Side Rendering and F# - It's Not Just for Scientists. I personally am planning on doing my Beginning Dependency Injection talk as well as something new that I am working on that is an Introduction to ORM and nHibernate.


 
Categories: Development | Events | Local | Shameless Plug