This article is the third in a series of articles not written by me, but by José F. Romaniello. He is a big NHibernate guy, so, he created a sample domain trying to evaluate how close the latest Entity Framework 4.1 Code First stuff is getting to NHibernate features,
Later, he chose to show how to do the same Code-First mapping using NHibernate and confORM, NHibernate mapping library that is created by Fabio Maulo, a primary developer in NHibernate source code.
I asked him to do it also with FluentNHibernate, so, he took the time and effort to create a nicely put visual studio solution. at some point he gave it to me as I was familiar with FluentNHibernate in general, not with automapping, which we wanted to use for this sample. so, I am now posting about this experiment.
This article’s very late than it should. Apologies to those who have been waiting.
Convention Based Mapping, AKA, Automapping
My audience is slightly different than Jose, I might need to explain this one. skip if not needed.
When you do Code-First (or Domain First with POCOs, or your favourite name), normally you need to define the mapping between you classes and DB tables class-by-class. In each class you define which table(s) it maps to and which properties correspond to which columns, etc..
Your mapping library, being Entity Framework 4.1 Code-First bits, NHibernate XML/HBM mapping bits, confORM, FluentNHibernate, etc… can give you some help with that by having some defaults for mappings. For example, assuming the table name is the same as the class name, properties map to columns with the same name, assuming your class a single column PK and is called “Id” or “<ClassName>Id’, etc..
Of course they might also allow you to modify those defaults to your likings, and to have exceptions to specific classes/columns to those defaults (conventions). The purpose still is to need as few such customizations as possible.
This is what was first introduced in FluentNHibernate as Automapping, and is also starting to get known as convention based mapping while other libraries apply it.
Let me steal borrow the class diagram Jose had for his test/review domain:
All needed to do was a NuGet command that brings FluentNHibernate and all its dependencies:
Remember, you don’t need to do it from the PowerShell console, right click the project and “Add Project Library Dependency” and finding the FluentNHibernate library in the wizard (select “Online” from the left and type FluentNHibernate in the top right text box) will simply do.
I did this just to show the dependencies easier in the picture. (note also I already had this in my project, so, created a new project “CleanFNhSample” just to show you this).
There is another dependency that i needed, not just because Jose used (and created)., but also because it’s really convenient also. NHibernate by default uses custom types for collections of type “set”, because those were not supported before .NET 4.0.. Jose has a code file NuGet package that makes using the built-in .NET 4.0 HashSet type instead of the custom type possible. it’s called “NHibernate.SetForNet4”:
Like the other packages, I could have added this from console. just showing you both.
The Application Code
I’ll be showing snippets of the code here, then later giving you a GitHub URL to play with it as you wish .
The main test code Jose created uses the domain classes in the diagram above is pretty simple. It creates a new NHibernate (NH) configuration (including class mappings to database), uses the mappings to create schema in the database (replacing anything that might exist, yes. This is not the only option), and creates an NH session (equivalent to EF DataContext if I over-simplify it), creates some records, and tries to save them to DB.
Now before we get into “ConfigureNHibernate()” method which is mainly the most important method here, let’s look at some other classes…
This is how my project looks right now:
The only difference from Jose’s confORM review and even his original FluentNHibernate work is the “Mapping” folder. While the few ConfORM samples used to have different methods in the same file to do the mappings, FluentNHibernate people seem to prefer (or I think they do) having a different class per part of the mapping. Of course you can still have those classes in the same file and no one would even complain!
So, there are 3 FluentNHibernate classes: in there. We’ll see why those are needed after we look at the main configuration. Note that not all this configuration is required as we were trying to meet specific requirements that were met in confORM demo.
Each part exists for a specific purpose that in mentioned in the comments above it.
I hope the comments are enough. We need to set some mapping rules in StoreConfiguration class, apply some non-default standards for our collections in CollectionConvention class, and some exceptions for a field of Order entity in the OrderOverride class.
I know you might be worried about the big method, and the fluent API that makes the whole thing is one statement. From my experience, this makes debugging each part of the statement that might fail a problem, but this is not the case here, because, NH configuration / mapping problems only show when you build configuration or create session factory anyway. It doesn’t fail on the individual steps. But don’t worry about that too, usually (but not all the time), when you navigate the InnerException (sometimes recursively through multiple inner exceptions), you get specific information what part was wrong and maybe what was wrong about it too.
The way you write conventions, overrides, and other kinds of FluentNHibernate stuff is by creating classes that implement certain interfaces or inherit from certain base classes. There are so many interfaces provided but you only need to implement the ones that allow you to provide other defaults than FluentNHibernate own defaults. Also, a lot of the features in the classes could have been added to configuration method, but you don’t want it to grow even bigger, at least not for this explanation sample!
In this class we do not implement an interface, instead, we derive from the default mappings class, which defines all defaults in automapping. This one was originally created by Jose and for myself I’m not sure whether the typical use is by having such a class or doing it directly in configuration.
What this method does is specify which classes represent DB entities and should be mapped automatically. It tells FNH to map all types in the EntityBase class namespace, except Enums and except EntityBase itself.
If I understand correctly, the part that we should have needed is only the namespace part. in “ShouldMap” and the “IsConcreteBaseType” (which basically tells FNH to not try to create separate table for EntityBase and make all other tables reference it for their PK. Instead, it should treat EntityBase as part of its children classes themselves as if the “Id” property was implemented in each of the classes not in their base class). I believe we shouldn’t have needed to exclude the enum explicitly (if it’s there the configuration is invalid, not some easier effect like having a table for enum values).
As mentioned, conventions are applied by implementing I….Convention interfaces, and there are many of them to override whatever defaults you might want. In this example, if we wanted our collection convention to only subset of collections we could have also implemented “ICollectionConventionAcceptance”. You can implement as many I<SomeCriteria>Convention and <SomeCriteria>ConventionAcceptance in the same class as you like.
Here we tell NHibernate to apply cascading to all (you can only restrict it to updates or deletes or none, check NH docs). We also tell FNH that all collections we use will be of type “set”. This is different from what we had in the configuration because the configuration part tells NHibernate what actual type to use when we want our collections to be sets, and that’s why it’s not per-class or per-collection (note the conventions are like mappings for each collection but done all at once), The convention here makes NHibernate understand to treat it as “set” and use the Collection Factory we provided in the configuration for dealing with sets.
Also note that usually I had to add extra code to check if the collection is collection of string or int or similar non-entity type and have the instance.Name(“Value”) or else it doesn’t work. Just while writing this article I tried it and found that I didn’t need to add it anymore (works without it normally).
If you have ever worked with normal ClassMap<TEntity> in FluentNHibernate, this one is going to be familiar. Having an interface instead of base class, and using an “Override” method instead of constructor, and having to use the parameter mapping (or whatever you call it) instead of calling “Map” methods directly are the only differences.
If you are not, then simply those overrides classes places to add mapping for the specific classes and properties that have special cases, without having conventions that apply to many other classes at the same time. Here we had a read only property in the Order class and we wanted to set its access (property, field, none, …) to read only. Jose and I thought it might be good example of overrides. This is also the place if you use special SQL formula for one specific property or so.
NuGet And Resources
Now, as promised, you can have the code on GitHub so that you can clone, fork, or simply download it and have a look.
The Code URL is:
I’d highly recommend you not only browse the code online, but also use TourtoiseGit or whatever client you may feel friendly (including console) to bring the code locally and start playing with it
However, if you insist on just old-school download-the-code style, no problem, here is the direct download link:
Sorry for the length of the post and I hope it wasn’t a show stopper that made you just skip to end.