Today we've struggled with NHibernate + Generics + Collections. The typical problem in NHibernate collections is that when you add relation using property, collection is not updated. For example, we have two classes Project and Release. Project has a collection of Releases. So, the following test will fail:
Project project = new Project(); Release release = new Release(); release.Project = project; project.Save(); // this is our wrapper for session.Save... Assert.AreEquals(1, project.Releases.Count)
Also you can't just add release to project via collection, the code below will not save anything:
Project project = new Project(); Release release = new Release(); project.Releases.Add(release); project.Save();
To have Releases collection in place, you should re-read project from database, which is not very good in general case.
We are not alone with this problem and quite cool solution has been implemented by Ayende, it is NHibernate Generics.
We've take the solution and tried to incorporate it in our framework. That was not very easy and we've learn some gotchas. First of all, our application already has about 20 classes and changes should be made in each class. However, that was not a large problem, since:
- We use code generation
- We have about 150 unit tests
This was major refactoring and it took only 1 full day. I don't know how much time such refactoring will take without code generation and tests, maybe a week... But most possible after 2-3 days team will revert code back 🙂
The most difficult thing was templates for code generator. We generate base classes for entities, and generator logic is was not obvious. For example, base constructors should have delegates in one cases and should not have them on another. Also we have custom method SaveByMerge that we use to save entities by ObjectDataSource that were edited in FormView. This method broke several tests. The logic of the method is quite natural, but it did not work as expected. Let's say you want to update project entity. You open edit form, type new project name and click Save button. System creates new project entity (it plays role of data transfer object in fact) and pass this new project from UI to business layer as well as actual project that should be updated. SaveByMerge checks all changed fields, sets new values for actual project and save it. The problem was in collections update in this method. When collection copied from DTO project to real project, all elements of collection in DTO removed and NHibernate throws an exception about re-saving deleted entities. The only solution we found is comment one line of code in Ayende library to not invoke item removal in this case.