nHibernate is getting there

Phew! This is a hard thing to do.

I have done two major things lately. First, I made it so that I can generate database schema based on nHibernate mappings as well as generate update scripts that will revise the database to match changes in the nH mappings. This latter is one of the things I am most excited about. I can't tell you how annoying I have found it over the years to try to keep code and database schemas in sync. 

The other is that I have managed to write data using nHibernate. I haven't got all functions shaken down yet, but it's getting close.

The main trick for schemas was getting mapping to work. The nH docs are not very instructive, especially to a C# newbie. I had the devil of a time figuring out (and eventually just asked and got the answer; I wish I was rich enough to just pay someone so I didn't feel obligated to make a good faith effort to find out on my own) how to deal with object properties that are objects themselves. In this case, I had a property called name, that was a BaseName type with it's own properties of First, Last, etc. Turns out that the <component> element is the one to use. Once I heard that, the example of how to use it was for a Name object. 

The other thing that was hard was to deal with many-to-many. There are obvious provisions for this in the nH docs but it isn't really explained. The main thing is that I had to add a property to my class for the elements of the related collection. That is, if Widgets and Goodies are in a many-to-many relationship (maybe I should have chosen People and Bars, many People visit each Bar has many People visiting it) then you will, at some point need to assign one to the other, eg,

firstWidget.barList.add(someBar);
firstWidget.barList.add(anotherBar);

I had to learn how to create firstWidget.barList. This is probably easy for experienced C# people. For me, I had to started learning about enumerable types. List<Widget> (and List<Goodie>) work for schema generation. when I tried to write data, I got, "Unable to cast object of type." Ouch.

Turns out that nH, behind the scenes, changes the type of that grouping property (actually, I used the <bag> element) to its own kind of enumerable type. "List" is actually a type based on the interface, "IList". I don't thoroughly understand this typing business, but nH can't change out an actual type but it can assign a type to a property specified with an interface.

Bottom line, I used IList<Widget> and IList<Goodie> and Voila! I got me some database writing.

But there's more!

These are smaller things that I knew about, but that came to bite me anyway. First, because of the whole lazy loading thing, properties need to be "virtual". This was easy to debug because the error message was very explicit. In my case, I am working with an industry xsd-based type system so I got to use me some serious regular expression mojo to make it happen.

My text editor of choice is BBedit. I needed to change all the public properties from "public PropertyName" to "public virtual PropertyName". Unfortunately, the classes are public as well (public partial class BlahBlah{}) so I had to exclude the public classes from the substitution. Eventually I found someone that had constructed a conditional regex pattern that did the job:

     (?(?=^(the_only_string_you_dont_want_to_match)$)^$|.*)

Since it was constructed to deal with an entire line, I changed it to:

     public +(?(?=(partial)) |.*)

and replaced it with

     public virtual \1

I have run into the other major issue several times and so, when I hit it again, it was easy. The xml mapping files, class.hbm.xml, property needs its output directory to be set as an Embedded Resource. If they are, things work. If not, "No persister for ...".

In a closely related point, remember to set your xml file property Build Action to "Copy Always."

Onward!!