As I mentioned in my previous post, Fluent NHibernate offers two different ways to realize your mappings in C# code:

  • - adding a Map-class per entity and write out the mapping in C#
  • - use the Automapping feature in Fluent NHibernate and let the framework do all the work for you
  • In this post I’ll show you how you can write a basic mapping of an entity using a ClassMap.

A simple example

I’m starting out with a very simple example, with just one entity and a value object. I’ll be extending this tiny domain a bit, step by step (probably in another post).

fluentmodel

In my DB, I just have one table, Person. It contains all the address-data, but in my domain, I want to map that data into a seperate object, a value object. If you were using NHibernate, you could do just that using the “component”-tag in ugly XML.

Using classic mapping, you create a new class, PersonMap, which should derive from the generic class, ClassMap.

Here’s the code:

public class PersonMap : ClassMap<Person>
{
	public PersonMap()
	{
		Id(x => x.PersonId);
		Map(x => x.FirstName);
		Map(x => x.LastName);
		Map(x => x.PhoneNumber); 

		Component<Address>(x => x.Address, m =>
		{
			m.Map(x => x.Street);
			m.Map(x => x.Number);
			m.Map(x => x.ZipCode);
			m.Map(x => x.City);
			m.Map(x => x.Country);
		});
	}
}

If you want to specify the length of the FirstName property as in your database, or constraint the property to never allow NULL, you can do this as follows:

 Map(x => x.FirstName)
   .CanNotBeNull()
   .WithLengthOf(100);

I mentioned in my introduction post that FNH also gives you the ability to code by convention. It states that a developer should only specify the items that don’t fit within the default behaviour, thus to limit the configuration to a minimum. This makes your code easily maintanable since you don’t have configuration code in which you can get lost, you only have what you need.

Let me demonstrate that with an example. Check out how I mapped my Id property in the ClassMap. Notice I didn’t specify anything at all? Well, that’s because FNH assumes you’re using the identity generator. If you’re not, you can override this by specifying another generator type for your ID. Thus, you will only need to specify how you’ll be generating your ID’s if your not using identity.

Why? This avoids a lot of setup code when all your ID’s use this generator, don’t you think? Still, the flexibility remains. If you want to use another generator type, or explicitly state you are using the identity generator, you can still do so.

New way of testing

First of all, just a note. When I’m playing around with something new, just as now I’m playing around with FNH, I almost never write an application that actually does something. I’m just adding unittests to check if what i’m doing works.

FNH introduces a new way of testing your mappings. Remember how we used to create integration tests that add data to the database, retrieve it afterwards to check if it was successfully inserted? Remember how it was frustrating to notice in those tests that you messed up your NH mapping?

Actually, this was my way of testing if I did my mappings right. If any CRUD-operation failed, it was almost abvious I messed up my mapping somewhere. We have a test “CanAddPerson” which is also responsible to check if your mappings our correct. That’s not good, but there wasn’t another way…

Well, now you can separate the testing of your mappings, and your actual integration tests.

Here’s how you test your mappings:

[TestMethod]
public void CheckPersonMappingIsValid()
{
	new PersistenceSpecification<Person>(new SessionSource(new TestModel()))
		.CheckProperty(x => x.FirstName, "Laila")
		.CheckProperty(x => x.LastName, "Bougria")
		.CheckProperty(x => x.PhoneNumber, "0498123456")
		.CheckProperty(x => x.Address, new Address("My street", "34", "BE-2000", "Antwerp", "Belgium"))
		.VerifyTheMappings();
}

And here’s the integration test which only has the responsibility to check whether we can add Person’s to the datastore or not.

[TestMethod]
public void CanAddPerson()
{
	Person personToAdd = new Person()
							 {
								 Address = new Address("street", "15A", "BE-2100", "city", "country"),
								 FirstName = "Laila",
								 LastName = "Bougria",
								 PhoneNumber = "0497123456"
							 };

	Session.Save(personToAdd);
	Session.Flush();
	Session.Clear();

	// use session to try to load the person
	var fromDb = Session.Get<Person>(personToAdd.PersonId);

	// Test that the person was successfully inserted
	Assert.IsNotNull(fromDb);
	Assert.AreNotSame(personToAdd, fromDb);
	Assert.AreEqual(personToAdd.Address, fromDb.Address);
	Assert.AreEqual(personToAdd.FirstName, fromDb.FirstName);
	Assert.AreEqual(personToAdd.LastName, fromDb.LastName);
	Assert.AreEqual(personToAdd.PhoneNumber, fromDb.PhoneNumber);
}

I ran the tests, and they both pass, so that’s great!

A note about value objects and fluent nHibernate

In my Person example, I’m mapping the address-data with a value object. In the model you’ll see it implements IEquatable.
Of course it does, you’ll think, it’s a value object! I just want to warn you, that if you think “I’ll implement the interface later…” and you run the mapping test, it wil fail. The VerifyMappings method will be comparing the Address instance and will expect true if all the properties match. If you didn’t implement the interface yet, your test will fail.

Share it:
  • Kick it!
  • DotNetShoutout
  • Technorati
  • DZone
  • TwitThis
  • Facebook
  • LinkedIn
  • del.icio.us
  • Digg
  • Reddit
  • Google
  • E-mail this story to a friend!