This is the second of a series of articles on the topic. Other posts will follow
In my last post I introduced a new framework which gives you the possibilities to define the mappings of the entities to the underlying database in C# using a fluent interface instead of writing XML mapping files. This has caused some discussion in the community on the pros and cons of this approach.
Let me repeat what the goals of this new framework are or should be
Now let's have a look at some common scenarios which you encounter daily in a moderately complex domain model. Let's concentrate on how these scenarios are mapped by using the fluent interface.
In DDD you have the notion of entities and value objects. The latter are immutable and have no identity. In NHibernate they are mapped as Component and its fields are embedded in the same table as the containing entity.
A typical value object is Money which represents a monetary value. Let's define the following simple domain model
We have an account which is an entity and the account contains a property of type Money (called Balance). Balance is now a value object and contains not only the amount but also the currency in which the value is expressed (Note: 100 US$ is different than e.g. 100 €). The code for the money class
public class Money
{
private Money()
{
}
public Money(decimal amount, string currency)
{
Amount = amount;
Currency = currency;
}
public virtual decimal Amount { get; private set; }
public virtual string Currency { get; private set; }
}
Note that the properties are read only since any value object is immutable (once set you cannot change it). The private parameter-less constructor is there ONLY to satisfy NHibernate which needs it. But since it is private we cannot accidentally use it.
The code for the Account class is trivial
public class Account : Entity
{
public virtual string Name { get; set; }
public virtual Money Balance { get; set; }
}
Note that Account inherits from the Entity base class which is implemented in the framework. As discussed in my previous post I regard this as a limitation of the framework. This requirement possibly will be eliminated in future versions of the framework.
How can we map this scenario? Let's have a look at the necessary code
public class AccountMap : ClassMap<Account>
{
public AccountMap()
{
Id(x => x.Id);
Map(x => x.Name)
.CanNotBeNull()
.WithLengthOf(50);
Component<Money>(x => x.Balance, m =>
{
m.Map(x => x.Amount, "BalanceAmount");
m.Map(x => x.Currency, "BalanceCurrency");
});
}
}
Easy, isn't it? First we map the property Id of our account entity (inherited from the base Entity class). Then we map the Name property. We declare that it cannot be null and that it's maximal length should not exceed 50 characters.
Finally we map the Balance property which is a value object an thus treated by NHibernate as Component. Here I have used the second (optional) parameter of the map function which is the name of the table column to which the respective property should be mapped. When we create the database schema from this mapping it will contain one table called Account which contains the four columns Id, Name, BalanceAmount and BalanceCurrency.
How does this fit for a scenario where I have an entity which has more than one property of the same value object type? Let's have a look at the following simple model. An Employee entity has a HomeAddress and a WorkAddress field. Both fields are of type Address. Address is a value object.
Again the code for the Address value object (which is immutable!)
public class Address
{
public virtual string AddressLine1 { get; private set; }
public virtual string AddressLine2 { get; private set; }
public virtual string PostalCode { get; private set; }
public virtual string City { get; private set; }
public virtual string Country { get; private set; }
private Address()
{
}
public Address(string addressLine1, string addressLine2, string postalCode, string city, string country)
{
AddressLine1 = addressLine1;
AddressLine2 = addressLine2;
PostalCode = postalCode;
City = city;
Country = country;
}
}
and the Employee entity
public class Employee : Entity
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual Address HomeAddress { get; set; }
public virtual Address WorkAddress { get; set; }
}
Now let's have a look at the mapping code
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.Id);
Map(x => x.FirstName).CanNotBeNull().WithLengthOf(20);
Map(x => x.LastName).CanNotBeNull().WithLengthOf(20);
Component<Address>(x => x.HomeAddress,
a =>
{
a.Map(x => x.AddressLine1, "Home_AddressLine1");
a.Map(x => x.AddressLine2, "Home_AddressLine2");
a.Map(x => x.PostalCode, "Home_PostalCode");
a.Map(x => x.City, "Home_City");
a.Map(x => x.Country, "Home_Country");
});
Component<Address>(x => x.WorkAddress,
a =>
{
a.Map(x => x.AddressLine1, "Work_AddressLine1");
a.Map(x => x.AddressLine2, "Work_AddressLine2");
a.Map(x => x.PostalCode, "Work_PostalCode");
a.Map(x => x.City, "Work_City");
a.Map(x => x.Country, "Work_Country");
});
}
}
Note that we have to explicitly name the underlying columns here (e.g. "Home_City" versus "Work_City") otherwise the schema generation would fail.
Honestly, I don't like the above code since it is not DRY. So let's refactor it...
public class EmployeeMap : ClassMap<Employee>
{
private Action<ComponentPart<Address>> MapAddress(string columnPrefix)
{
return a =>
{
a.Map(x => x.AddressLine1, columnPrefix + "AddressLine1");
a.Map(x => x.AddressLine2, columnPrefix + "AddressLine2");
a.Map(x => x.PostalCode, columnPrefix + "PostalCode");
a.Map(x => x.City, columnPrefix + "City");
a.Map(x => x.Country, columnPrefix + "Country");
};
}
public EmployeeMap()
{
Id(x => x.Id);
Map(x => x.FirstName).CanNotBeNull().WithLengthOf(20);
Map(x => x.LastName).CanNotBeNull().WithLengthOf(20);
Component<Address>(x => x.HomeAddress, MapAddress("Home_"));
Component<Address>(x => x.WorkAddress, MapAddress("Work_"));
}
}
I have extracted the mapping of the address into a helper method and can now call it as many times as I have to and just have to provide the table column prefix to be used for all fields of the address. What I still don't like in the above approach is that I need an internal helper method to map my addresses. What if I have another entity (say Customer) which also has one or several properties of type Address?
It would be nice if the framework would provide some component mapper.
Since I cannot rely on the framework at the moment I have found the following more elegant solution, where the mapping of the Address value object is externalized into it's own mapper class
public class AddressMap
{
public static Action<ComponentPart<Address>> WithColumnPrefix(string columnPrefix)
{
return a =>
{
a.Map(x => x.AddressLine1, columnPrefix + "AddressLine1");
a.Map(x => x.AddressLine2, columnPrefix + "AddressLine2");
a.Map(x => x.PostalCode, columnPrefix + "PostalCode");
a.Map(x => x.City, columnPrefix + "City");
a.Map(x => x.Country, columnPrefix + "Country");
};
}
}
and then the Employee mapper class can be simplified to this
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.Id);
Map(x => x.FirstName).CanNotBeNull().WithLengthOf(20);
Map(x => x.LastName).CanNotBeNull().WithLengthOf(20);
Component(x => x.HomeAddress, AddressMap.WithColumnPrefix("Home_"));
Component(x => x.WorkAddress, AddressMap.WithColumnPrefix("Work_"));
}
}
Well, I'm now quite happy with my solution.
Now let's write a test for the mapping. A simple test which verifies that the mapping is correct and that a record can be written to the database is quite easy to implement
[TestFixture]
public class Employee_Fixture : FixtureBase
{
[Test]
public void Verify_that_employee_saves()
{
var emp = new Employee
{
FirstName = "Gabriel",
LastName = "Schenker",
HomeAddress = new Address("Castle home", null, "8888", "Paradise", "Switzerland"),
WorkAddress = new Address("My work place", null, "7777", "Atlantis", "Pegasus")
};
Session.Save(emp);
Session.Flush();
Session.Clear();
var fromDb = Session.Get<Employee>(emp.Id);
Assert.AreNotSame(emp, fromDb);
Assert.AreEqual(emp.FirstName, fromDb.FirstName);
Assert.AreEqual(emp.LastName, fromDb.LastName);
Assert.AreEqual(emp.HomeAddress.AddressLine1, fromDb.HomeAddress.AddressLine1);
Assert.AreEqual(emp.HomeAddress.AddressLine2, fromDb.HomeAddress.AddressLine2);
Assert.AreEqual(emp.HomeAddress.PostalCode, fromDb.HomeAddress.PostalCode);
Assert.AreEqual(emp.HomeAddress.City, fromDb.HomeAddress.City);
Assert.AreEqual(emp.HomeAddress.Country, fromDb.HomeAddress.Country);
Assert.AreEqual(emp.WorkAddress.AddressLine1, fromDb.WorkAddress.AddressLine1);
Assert.AreEqual(emp.WorkAddress.AddressLine2, fromDb.WorkAddress.AddressLine2);
Assert.AreEqual(emp.WorkAddress.PostalCode, fromDb.WorkAddress.PostalCode);
Assert.AreEqual(emp.WorkAddress.City, fromDb.WorkAddress.City);
Assert.AreEqual(emp.WorkAddress.Country, fromDb.WorkAddress.Country);
}
}
[Test]
public void Verify_that_employee_saves_revisited()
{
new PersistenceSpecification<Employee>(Session)
.CheckProperty(x=>x.FirstName, "Gabriel")
.CheckProperty(x=>x.LastName, "Schenker")
.CheckProperty(x => x.HomeAddress, new Address("Castle home", null, "8888", "Paradise", "Switzerland"))
.CheckProperty(x => x.WorkAddress, new Address("My work place", null, "7777", "Atlantis", "Pegasus"))
.VerifyTheMappings();
}
You can get the source code of the solution accompanying this post here.
In this post I discussed the mapping of complex entities having one to many properties which are value objects. I have shown that a mapping is possible with the current release of the mapping framework.
Enjoy