When I wrote the last post, I had gotten nHibernate to create objects. They were specified to be in a many-to-many relationship and, though I can't remember the exact details of that moment, the objects were being created but the relationship was not. Since then, I have been really struggling to create that relationship.
The biggest thing that I have learned is that many-to-many is no longer fashionable and, I think, appropriately so. It is replaced by symmetrical one-to-many associations with a single relationship table. That is, a person visits many bars, and a bar has many visitors, but they really have a codependent relationship with each other. Which is to say, a person has many codependent relationships that help him be drunk and the bar has many codependent relationships that help it contribute to the delinquency of the culture.
For me, the nicest thing about this is that it removes the behind the scenes magic. It also makes use of that joining table. After all, it's not like there is some special kind of table for this. It's a table with data and making it a relationship rather than something invisible makes it more useful by giving us some semantics that allow us to store data about the relationship. The drunken person might want to note the number of blackout experiences enjoyed in each codependent relationship. The bar might want to record how many times police were involved for any particular codependency.
When I worked out how to use nHibernate <bag> elements and the <many-to-one> element and which entity they refer to (simple but slippery), I got an error message, "Cannot insert the value NULL into column." The column referred to is the foreign key in the join table. It's as if nH tries to create the join table first, before it knows the private keys of the other two records. Since I am using MSSQL, it allows you to specify whether a field is nullable and, nHibernate is really keen on using that when it generates a schema.
I communicated with the nice people on the google group (If you want to see the details, look at the technical conversation here) and tried many things. Inverse="true" did no good. Using <ibag> neither worked nor made sense. I tried using <set> just in case (couldn't figure out the type of the object property for that and it makes no sense because there can be duplicates and no sequence is implied). I tried using interlocking many-to-many. I tried changing the order of creation and saving in the app. Nothing mattered.
The only way I made any progress was to tell the database that the field is nullable but then, it complained that I was violating a "foreign key constraint". Sure enough, reading the generated schema, nHibernate adds this to the database, too. A foreign key constraint basically tells the database what two table/column pairs relate to each other and will not allow that to be screwed up. Good idea, unless you are using an ORM that creates the objects first and subsequently updates the foreign keys. I suspect that the update is when the violation occurs. I was far enough into it then that I knew there was no way I was going to convince nH to do it differently. I edited the schema again and Voila! Two concrete objects and a join table with correct foreign keys.
Tough luck on the database constraints.
Finally, as I write this, I am thinking about that guid. It's a long string and it's used as a key. I had chosen an nHibernate generator type that turned the guid into a string (uuid.hex) and can see that there is nothing that can be alphabetized about them. This is worrisome. I looked at the other guid generator schemes in nH (here) and found one that had a link to the rationale behind it. First, the article confirmed my suspicions. In simple tests, a guid is takes thirty times longer than an integer key. Big problem. This guys new guid generator makes for good, local commonality in the beginning characters. This reduces the penalty from thirty to .1, a 300x reduction. Unfortunately, using this method (guid.comb) gives me a type conversion violation in C#. I guess I have more work to do.