Wednesday, 25 March 2009 09:52 by
Hadi
A couple of days ago, there was a post by Howard Dierking covering the topic of Fluent NHibernate. What caught my attention was one of the comments:
I find this a little weird... isn't it so that in general, nhibernate supporters loath the 'domain model following the database tables' approach, while to get automapping, you practically have to? ;)
About the PI issue: there's no such thing as persistance ignorance: whatever persistence logic you're using, it bleeds into your own code one way or the other, be it a base class, be it requirements how to define properties/members, be it how to deal with 2-sided relationships etc.
I want to focus the following series of posts on showing what Fluent-NHibernate (NHibernate) is truly about, and let you be the judge of whether the previous comment is correct or not. My stand on it is that it's not.
So without further ado, let's start by the basics.
[Note: Due to time limitations and also to keep posts short, I will try and focus on small sections on each post]
NHibernate 101 in one paragraph
For those new to NHibernate, it is an ORM framework (Object Relation Mapper). ORM's try and eliminate the impedance mismatch that exists between Object Orientated programming and Relational Databases. This allows developers to work with objects and not have to worry about how these objects are persisted to disk in a relational database (i.e. tables, joins, etc.). Other examples of ORM's include LLibGenPro, Linq to Sql, and of course Entity Framework (although some people claim that EF is MORE than a ORM, but each to their ow).
Mapping in NHibernate
One of the main problems with any ORM is that there is considerable amount of time spent mapping objects to their corresponding tables in a database. Imagine the following class:
1: public class Customer
2: {
3: public virtual int Id { get; private set; }
4: public virtual string NameFirst { get; set; }
5: public virtual string NameLatst { get; set; }
6: public virtual string Telephone { get; set; }
7: public virtual string Email { get; set; }
8: }
This needs to be stored in the database somehow, and to do so, the framework (in our case NHibernate), needs to know how to match objects to tables and properties to columns. To solve this, NHibernate uses the concept of mapping files (hbm), which are non other than XML files with specific tags:
1: <?xml version="1.0" encoding="utf-8" ?>
2: <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
3: namespace="Examples.Domain" assembly="Examples.Domain">
4:
5: <class name="Customer" table="Customer">
6: <id name="Id">
7: <generator class="identity" />
8: </id>
9: <property name="NameFirst"/>
10: <property name="NameLast"/>
11: <property name="Telephone"/>
12: <property name="Email"/>
13: </class>
14:</hibernate-mapping>
The configuration is pretty self-explanatory. You define the name of the class, the corresponding table name you want in the database, and then start to define a series of properties. NHibernate follows a series of conventions, so for example if a column attribute is not specified in the property element, the corresponding column name in the database table will be the same as that of the property. The same thing happens with the type of the property, again, NHibernate internally maps CLR types to database types.
One of the problems with this type of mapping is that you don't get type-safety. If you make a mistake in writing out a column name you won't know of the issue until runtime. The other problem occurs when you are trying to refactor your code. If you don't have special add-ons such as Resharper (and if you don't, what are you waiting for?), refactoring XML files is pretty much a no-no in Visual Studio. Imagine the scenario: you rename NameFirst to FirstName and forget to do it in your mapping file. Your application will still compile, but come runtime, kaboom!
Fluent-NHibernate
This is where Fluent-NHibernate enters the picture. This project was initially built to allow for developers to map their entities to tables in code, i.e. using C#.
1: 1. public class Customer : ClassMap<Customer>
2: 2. {
3: 3. public Customer()
4: 4. {
5: 5. Id(x => x.Id);
6: 9. Map(x => x.NameFirst);
7: 9. Map(x => x.NameLast);
8: 9. Map(x => x.Telephone);
9: 9. Map(x => x.Email);
10: 2. }
11: 3. }
Already this provides benefits. By allowing this, you get away from the magic strings in the mapping files, allowing not only for compile-time checking but also refactoring.
However, one thing still remains, mapping. You still have to map your classes. Now, during both the mapping using XML as well as C#, have you noticed one thing? A series of assumptions are made. We said previously that you don't need to specific column names or types, etc. These are conventions. NHibernate assumes a series of conventions. Following the 80/20 rule, assuming that 80% of the time these conventions suit our applications, there's a remaining 20% that would need some sort of adjustment.
Auto Persistence Model
Say Hello to the Auto Persistence Model in Fluent-NHibernate. By latching on to the concept of convention over configuration, the Auto Persistence facilities in Fluent-Nhibernate do the mapping for you. This makes a series of decisions and maps your classes to their corresponding database tables and columns. Yet at the same time it allows you to tweak these conventions. This then gives us 100% coverage. For 80% of the time, the APM is fine, and for those special cases we adjust the conventions to our needs. Here is the same mapping using the Auto Persistence Model in Fluent-NHibernate:
1: AutoPersistenceModel.MapEntitiesFromAssemblyOf<Customer>().Configure(config);
AutoPersistenceModel has a static method MapEntitiesFromAssemblyOf where you pass in a specific type. Since my current example only has one type, namely Customer, I'm passing this one in. It takes this assembly and loads all the types it contains and create the NHibernate mappings for you. It places these mappings into a NHibernate Configuration class (the config parameter).
So far, the code for the simple example project is limited to the Customer class and the single isolated line of code that generates the mapping using Fluent-NHibernate's Auto Persistence Model. However, on it's own it's pretty useless. We need to somehow tie it to a database (that doesn't exist yet) so that we can actually make use of it. To do so, we create a Mapper class that generates the mappings and returns the NHibernate configuration:
1: public static class Mapper
2: {
3:
4: public static Configuration GenerateMapping(string connectionString)
5: {
6: var config = MsSqlConfiguration.MsSql2005
7: .ConnectionString(c => c.Is(connectionString))
8: .ConfigureProperties(new Configuration());
9:
10: AutoPersistenceModel.MapEntitiesFromAssemblyOf<Customer>().Configure(config);
11:
12: return config;
13:
14: }
15:
16: }
What this class is doing, is create a NHibernate configuration that is based on Microsoft SQL Server (2005 and up) and configures a series of default properties. The only parameter it requires is the connection string. It generates the the mapping files and returns the configuration to us. Next step is to make use of this configuration. Remember, right now we have the mappings, but we don't actually have any underlying database. We don't know anything about how the structure is or should be.
Creating the database
In the NHibernate framework, there is a namespace called hbm2ddl which as it's name indicates, can create DDL from mapping files. That is precisely what we need. To use it, it's as simple as passing in a NHibernate configuration to the SchemaExport class:
1: using NHibernate.Tool.hbm2ddl;
2:
3: namespace Example.DBGenerator {
4:
5:
6: class Program
7: {
8: static void Main(string[] args)
9: {
10: var config = Mapper.GenerateMapping(args[0]);
11:
12: new SchemaExport(config).SetOutputFile(args[1]).Create(/* Output to console */ false, /* Execute script against database */ true);
13: }
14: }
15: }
Ignoring the fact that there is absolutely no error checking in this application whatsoever, all we are doing is taking the our configuration generated by the mapper and passing it to our SchemaExport class, which is calling the Create method to create the SQL, dump it into a file and at the same time, generate it for us in SQL Server. Running this executable with the following command line:
"Data Source=.\SQLEXPRESS;Initial Catalog=FluentExample;Integrated Security=true" Output.sql
we get:
Summary
In this first post, I wanted to show you how you can easily create a domain model and have the corresponding database created for you automatically, without having to worry about any types of mappings, let alone database structures. However, I've only touched the tip of the iceberg. In the next series of posts, we'll drill down into everything Fluent-NHibernate has to offer us.
Coming back to the original comment, what do you think? Did I start with my domain object or my database? This is what true persistence ignorance is about!
Monday, 16 March 2009 01:50 by
Hadi
There's a common conception that developers suck at creating user interfaces. And it's probably right on target. One of the reasons is that, as developers, we look at things differently than a normal user would. And usually this causes problems, except in one scenario where it's an advantage: when our end users are of our own species. NHProf fits that bill perfectly.
Along with Balsamiq, which I blogged about a few days ago, NHProf is the other tool that I had a chance to play with this week, and I'm pretty excited about it. For those of you who don't know what it is, NHProf is a profiler for NHibernate, created by Ayende.
One of the characteristics of NHibernate is it's extensive logging support, and using log4net, NHibernate can log almost anything that happens. However, it's not always easy to read this information, let alone debug what's wrong.
NHProf tries to fix this shortcoming by offering a visual and extremely useful interface that provides relevant information you need to understand what's going on under the covers and help you improve performance of your application. And if that weren't enough, in a true fashion, inline with tools such as Resharper, it even offers you tips and recommendations.
I'll be posting more about this tool as I play with it. It's really got me excited. It comes with a 30 day trial so make sure you check it out.
Monday, 22 December 2008 12:01 by
hadi
The AutoPersistenceModel in Fluent NHibernate doesn't support values types automatically (yet), but getting them to work isn't that difficult. Take the following example:
1: public class Company
2: {
3: public virtual int Id { get; set; }
4: public virtual string Name { get; set; }
5: public virtual ICollection<Contact> Contacts { get; set; }
6: public virtual ICollection<Address> Addresses { get; set; }
7: }
8:
9: public class Contact
10: {
11: public virtual string NameFirst { get; set; }
12: public virtual string NameLast { get; set; }
13: public virtual string Telephone { get; set; }
14: public virtual string Email { get; set; }
15: }
16:
17: public class Address
18: {
19: public virtual string Street { get; set; }
20: public virtual string City { get; set; }
21: public virtual string State { get; set; }
22: public virtual string Country { get; set; }
23: }
Contact and Address are value types, that is, the only way to get to them is via the aggregate root, which is Company. Here's the AutoPersistenceModel of Fluent NHibernate:
1: persistenceModel.AddEntityAssembly(assembly)
2: .Where(entity => entity.Namespace == "iMeta.Examples.Domain.Entities" && entity.GetProperty("Id") != null)
3: .WithConvention(convention => convention.GetForeignKeyName = p => p.Name + "Id")
4:
5:
6: .ForTypesThatDeriveFrom<Company>(map =>
7: {
8: map.HasMany<Contact>(t => t.Contacts).Component( c =>
9: {
10: c.Map(t => t.NameFirst);
11: c.Map(t => t.NameLast);
12: c.Map(t => t.Telephone);
13: c.Map(t => t.Email);
14: }).AsList();
15: map.HasMany<Address>(t => t.Addresses).Component(c =>
16: {
17: c.Map(t => t.Street);
18: c.Map(t => t.City);
19: c.Map(t => t.State);
20: c.Map(t => t.Country);
21: })
22: .AsList();
23:
24: })
30: .Configure(config);
We are using telling the AutoPersistenceModel that for types that derive from Company (currently there is no support for inheritance but its named like this for future support), map the Contacts and Addresses properties to a collection of components. That simple!
I'm assuming, and hoping, that in future versions this will be supported automatically so there won't be a need for this.
Friday, 19 December 2008 10:05 by
hadi
I've recently started a new internal project and decided to use NHibernate. I've used NHibernate previously on several projects and one of the things I didn't like was the mapping. That's where you waste the most time since and for refactoring it sucks. Every time you need to create a new property or change an existing one, you need to make sure you've updated the XML mapping file.
I had decided to check out the fluent nhibernate project that I had heard about. I thought even though it didn't do away with the mapping, at least you had it in code, so you could easily refactor and do away with some of the mapping errors you initially face. Luckily before I started this project, I ran across Ayende's post on the new Auto-Mapping features of Fluent NHibernate, and I must say, I've been blown away (my twitter followers are probably starting to get sick of me).
I'm now able to do true TDD without having to touch a line of SQL or XML. The only reason I open up SQL Management Studio is to just make sure that the automapping is working correctly, and it is. It handles one to many, many to one, many to many, anything you could think of. And the beauty of it is, that if there is something it can't handle, you can still map it yourself in code.
Take this class:
1: public class Customer
2: {
3: public virtual int Id { get; set; }
4: public virtual string NameFirst { get; set; }
5: public virtual string NameLast { get; set; }
6: public virtual ICollection<Invoice> Invoices { get; set; }
7: }
8:
9: public class Invoice
10: {
11: public virtual int Id { get; set;}
12: public virtual string Number { get; set; }
13: public virtual DateTime Date { get; set; }
14: // Rest omitted...
15: }
The only thing I have to do, to configure this is:
1: persistenceModel.AddEntityAssembly(assembly)
2: .Where(entity => entity.Namespace == "iMeta.Example.Domain.Entities" && entity.GetProperty("Id") != null)
3: .WithConvention(convention => convention.GetForeignKeyName = p => p.Name + "Id")
4: .Configure(config);
That's it. The only thing that I don't seem to know how to handle yet is components. For now I'm adding some manual mapping (something I'm missing?). So for instance, if Customer has an Address property like so:
1: public class Address
2: {
3: public virtual string Street { get; set;}
4: public virtual string City { get; set;}
5: public virtual string State { get; set;}
6: }
and the corresponding property on Customer:
1: public class Customer
2: {
3: ...
4: public virtual ICollection<Address> Addresses { get; set; }
5: }
the resulting configuration would be (line 4 is new):
1: persistenceModel.AddEntityAssembly(assembly)
2: .Where(entity => entity.Namespace == "iMeta.Example.Domain.Entities" && entity.GetProperty("Id") != null)
3: .WithConvention(convention => convention.GetForeignKeyName = p => p.Name + "Id")
4: .ForTypesThatDeriveFrom<Customer>(map => map.Component<Address>(t => t.Addresses,
5: c =>
6: {
7: c.Map(t => t.Street);
8: c.Map(t => t.City);
9: c.Map(t => t.State);
10: }))
11: .Configure(config);
If you don't add this, it will create a table in the database of type Address and reference the customer with a foreign key, as opposed to flattening out Address to fields in the Customer table.
If you combine all this with a call to SchemaExport (Nhibernate Tools namespace), you can have it create your database schema for you. Do this in your tests and you no longer have to worry about cleaning up your database before each test or creating scripts that need to be run as part of your CI build. Very cool!