Hands on ConfORM: mixed mapping techniques

Print Content | More

In the last post we saw how to put ConfORM in action, this time..before digging into what you can do with it I would like to solve a problem that you (like me) will surely have to face if you plan to use ConfORM in a pre-existing project.

Let’s consider the typical scenario: you already have NHibernate up and running, but all your mappings are actually done using the standard XML mapping technique (which I still recommend to use when dealing with legacy databases). Now you need to extend the application and you have full control over the new tables that will be added, you don’t want to loose too much time rewriting all your mappings during the development stage; ConfORM is well suited to map this portion of your domain.

The main problem is that you do not want to touch what’s already working too much (that is you do not want to rewrite all your mapping to use ConfORM along all your application, because you have to define too many exceptions), plus some of your new classes can have references to portions of the domain mapped as XML and vice-versa...the question is: can I use a mixed mapping technique ?

The answer is: YES! Thanks to NHibernate and ConfORM great flexibility. Let’s see how modifying the example I had in my previous post.

Our domain consist of the following classes mapped using ConfORM: Person, Adult and Child.

We introduce a new entity called ‘Alien’ that we will map using XML file, and we’ll define some relationship between this class and the previous ones.

Here’s how we change the domain:

public class Alien
{
	public virtual int Id { get; set; }

	public virtual string Name { get; set; }

	public virtual Person HumanFriend { get; set; }
}

public class Child : Person
{     
	public virtual Adult Father { get; set; }

	public virtual Adult Mother { get; set; }

	public virtual Alien EtFriend { get; set; }
}

As you can see a Child can now have an Alien friend (remember ET ? ) and an Alien can have a human friend.

Here’s the mapping for the Alien object:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
						 namespace="ConfORM_Tests.Domain"
						 assembly="ConfORM_Tests">
	<class name="Alien" table="Alien">
		<id name="Id">
			<generator class="native" />
		</id>
		<property name="Name" />
		<many-to-one name="HumanFriend" />
	</class>
</hibernate-mapping>

We create and initialize the NHibernate configuration object the usual way we do when it comes to XML files:

Configuration nhConfig = ConfigureNHibernate();

nhConfig.AddAssembly(typeof(Alien).Assembly);
// initialize ConfORM engine
InitializeConfORM(nhConfig);

The ‘major’ changes are done in how we define the mapping shape in ConfORM:

private static void InitializeConfORM(Configuration nhConfig)
{
	var orm = new ObjectRelationalMapper();
	var mapper = new Mapper(orm);

	// define the mapping shape

	// list all the entities we want to map.
	// let's exclude the 'Alien' which will be mapped with a standard XML file 
	IEnumerable<Type> baseEntities = typeof(Person).Assembly.GetTypes()
		.Where(t => t.Namespace == typeof(Person).Namespace && t != typeof(Alien));

	// defines the whole hierarchy coming up from Person
	orm.TablePerClassHierarchy<Person>();

	// we map all the other classes as Table per class
	orm.TablePerClass(baseEntities.Where(t => !typeof(Person).IsAssignableFrom(t)));

	// specify the relation we have between Child and Alien otherwise conform will generate a property by default.
	// it has no real clue of what an 'Alien' is and we have to help him!
	orm.ManyToOne<Child, Alien>();
	orm.ManyToOne<Alien, Person>();

	// compile the mapping for the specified entities
	HbmMapping mappingDocument = mapper.CompileMappingFor(baseEntities);

	// dump the mapping to the console
	Console.Write(mappingDocument.AsString());

	// inject the mapping in NHibernate
	nhConfig.AddDeserializedMapping(mappingDocument, "Domain");
	// fix up the schema
	SchemaMetadataUpdater.QuoteTableAndColumns(nhConfig);
}

The key points are:

  • Line 10: we need to exclude the XML mapped entities from the list of classes we want to map with ConfORM.
  • Lines 21-22: we need to specify the kind of relation that will exists between the differently mapped classes, this must be done because ConfORM is not able to discover the relationship by itself. It has no clue of what an Alien is! If we do not help him he will generate a standard ‘property’ mapping for any object it does not know.
  • Lines 25-31: we add the ConfORM generated mapping to the NHibernate configuration. How flexible!

Done!

If we look at the generated mapping for the child object we can see:

...
  <subclass name="Child" extends="Person">
    <many-to-one name="Father" />
    <many-to-one name="Mother" />
    <many-to-one name="EtFriend" />
  </subclass>
...

Without the hint on the relations we gave to ConfORM it would have generated a <property name=”EtFriend” /> entry for the mapping.

We can now use NHibernate to generate our database and write a test like this and verify that it passes:

[Test]
public void T03_ChildAlien_TheyCanBeFriends()
{
	ISessionFactory sf = CreateDatabaseAndGetSessionFactory();

	// create the relation
	using (ISession s = sf.OpenSession())
	{
		int childId;

		using (ITransaction tx = s.BeginTransaction())
		{
			Alien alien = new Alien { Name = "Xfuncz" };
			s.SaveOrUpdate(alien);

			Child child = new Child { FirstName = "Teddy", LastName = "Strange", BirthDate = DateTime.Now, EtFriend = alien };
			s.SaveOrUpdate(child);
			childId = child.Id;

			tx.Commit();
		}

		s.Clear();

		using (ITransaction tx = s.BeginTransaction())
		{
			// get the data
			var loaded = s.Get<Child>(childId);

			tx.Commit();

			Assert.IsNotNull(loaded);
			Assert.IsNotNull(loaded.EtFriend);
			Assert.AreEqual("Xfuncz", loaded.EtFriend.Name);
		}
	}
}

I’m just amazed by the flexibility of NHibernate and ConfORM.

I haven’t tested all the possible cases or relations and with very complex object hierarchies I think you can still have trouble and you need to convert part of the mappings, but nonetheless it’s a very good starting point to introduce ConfORM in your existing applications.

The following file is a complete test project you can use as a reference for the previous example:



NHibernate, ConfORM

5 comments

Related Post

  1. #1 da Fabio Maulo - Wednesday October 2010 alle 12:36

    very useful example, I never tried it.
    Congratulation!

  2. #2 da Gian Maria - Wednesday October 2010 alle 02:31

    Very useful example, having the possibility to take the best from both mapping technique is a very interesting way of configuring NH.

    After years of usages, I'm still amazed on how flexible NHibernate is.

  3. #3 da Pete - Tuesday October 2010 alle 10:51

    I know you still recommend not using confORM with existing DB, but... :)

    I have been using Fluent NHibernate for mapping but would like to switch over to confORM. Our DB which consists of 450+ tables has an old naming convention. For instance student last name is stud_lname. In our Entity Object the property is called StudLname. This is consistent for all columns as they map to the Entity Objects. Is there a convention that I can give confORM so there is not an exception for every column on every table?

  4. #4 da alessandro giorgetti - Wednesday October 2010 alle 09:47

    Hi Pete, to my knowledge none of the already built conventions act that way, you can however extend the system and build your own. I'm working on a similar problem for an application I'm writing, when I've time to finish it, I'll post my solution maybe it could help you too.

  5. #5 da alessandro giorgetti - Thursday October 2010 alle 01:00

    I've managed to end the post about how to customize the names given to tables, properties and so on...
    Check the next part of this series at:
    http://www.primordialcode.com/blog/post/conform-pattern-customize-tables-properties-names

All fields are required and you must provide valid data in order to be able to comment on this post.


(will not be published)
(es: http://www.mysite.com)


  1. #1 da http://www.primordialcode.com/blog/post/nhibernate-conform-mapping-explorer

    www.primordialcode.com - NHibernate ConfORM Mapping Explorer