3
votes

I'm working on migrating an application to NHibernate, and I'm using Fluent NHibernate. I'm running into an issue mapping a collection of value types to an aggregate root.

I have a PhoneNumber value type that has a few properties — Number, NumberType, and Description. There are a number of objects in my domain that have collections of phone numbers.

In the database (SQL 2008), these values are held in different tables. So I might have Customers and CustomerPhoneNumbers as well as Vendors and VendorPhoneNumbers. The phone number tables are identical except for the foreign key that relates them to their parent.

The problem is that I would like to use a simple PhoneNumber value type without having to create CustomerPhoneNumber and VendorPhoneNumber types that have properties that relate them to their parent type, and I don't know how to accomplish that in NHibernate. Is this possible or do I need to change my domain objects to more closely match the underlying database schema?

UPDATE: A little more info
It looks like I'm having trouble even getting the basic map to work for retrieval. Here's a simplified example of what I have:

public class CustomerMap : ClassMap<Customer>
{
    public CustomerMap()
    {
        Table("Customers");

        Id(x => x.Id);
        Map(x => x.Name);
        Component(x => x.Address);
        HasMany(x => x.PhoneNumbers)
            .KeyColumn("CustomerId")
            .Cascade.All()
            .Table("CustomerPhoneNumbers");
    }
}

public class PhoneNumberMap : ClassMap<PhoneNumber>
{
    public PhoneNumberMap()
    {
        Id(x => x.Id);
        Map(x => x.Number, "PhoneNumber");
        Map(x => x.PhoneNumberType);
        Map(x => x.Description);
    }
}

It looks like the SQL isn't being generated properly. When I try to look into a customer's PhoneNumbers list, I get an ADO error that reveals that NHibernate is looking for a table named PhoneNumber.

It seems like it's not picking up the Table("CustomerPhoneNumbers") part and is defaulting to a table named the same thing as the object. Yet I can't specify a Table in the PhoneNumberMap, because it'll be different depending on which aggregate root we're pulling for.

1
may want to have a read at this thread goo.gl/D0RmN - used2could
That seems to be about composite keys, so I'm not sure that applies here. My biggest problem at the moment is that even though I specify a table name in the map class for the aggregate root, NHibernate is using either the table name in the value type map class or, if that's absent, assumes the table name is the same as the class name. - Josh Anderson
The reason it's defaulting to a table name with the same name as your object is because you don't specify the Table("TableName") in the PhoneNumberMap class. This doesn't solve your problem of having to map multiple tables to the same phone number object but hopefully there is a solution for that. - Cole W
It's easier to add Id for Your value objects that are used for ORM only and forget about components. - Arnis Lapsa
@Cole, if I specify the column in the PhoneNumberMap it will just use that table and still ignore the rule I specified in the aggregate root object. I need some way to have that single PhoneNumber class be persisted in multiple tables - CustomerPhoneNumber, VendorPhoneNumber, etc. - Josh Anderson

1 Answers

0
votes

You can map the single object PhoneNumber to multiple tables using the entity-name mapping attribute. Sorry, no sample code - I do not use Fluent so I cannot help with that aspect. I could post sample hbm mappings if that would be helpful.

Entity-name basically replaces class name across the board. When you use entity-name you will also have to modify your session to accept a parameter to specify the entity name whenever you work with the objects via the session.

Documentation is here:

http://docs.jboss.org/hibernate/core/3.2/reference/en/html/mapping.html#mapping-entityname

EDIT Added hbm samples.

This maps a single object PhoneNumber to multiple tables

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="DomainModel.PhoneNumber, DomainModel"
    table="PhoneNumberVendors" entity-name="PhoneNumberVendor">
    <id name="_id" access="field" column="PhoneNumberId">
        <generator class="assigned"/>
    </id>
    <property name= "...">
    </class>
<class name="DomainModel.PhoneNumber, DomainModel"
    table="PhoneNumberCustomers" entity-name="PhoneNumberCustomer">
    <id name="_id" access="field" column="PhoneNumberId">
        <generator class="assigned"/>
    </id>
    <property name= "...">
    </class>
</hibernate-mapping>

Then to call for example an update on a PhoneNumber you modify the session syntax to the following so nhibernate knows which table to use:

_session.Update("PhoneNumberCustomer", myCustomerNumber) 

You will have to figure out if Fluent even supports this, if so how to do it. If not you can always use hbm files for the PhoneNumber object.