views:

1050

answers:

3

I have an entity like:

public class Employee
{
    public int ID { get; set; }
    public IAccountManager AccountManager { get; set; }
    ...
}

I also have a mapping defined for "DefaultAccountManager" - a concrete implementation of IAccountManager. When mapping the above "Employee" entity, how do I tell NHibernate to persist/load the AccountManager property using the mapping defined in "DefaultAccountManager"?

Edit: Actually if I could setup a mapping for IAccountManager so that NHibernate could just infer which implementer to load/persist that would be even better. I'd rather not have to break polymorphism by forcing all implementers to use the same mapping.

A: 

If you need polymorphism because the functionality of an IAccountManager implementation may have different functionality you could look at discriminators and use a base-class instead of an interface.

If you are just using an interface because they are the OOP paradigm du jour then think twice about it, with entities usually carrying little to no behavior an interface provides little -- if any -- value in a situation such as this.

joshperry
I've heard the interfaces for entities = "no value" response a few times lately. Is NHibernate expecting that my entities are just DTO's with not other logic? Not totally comfortable with NHibernate yet so I'm just curious.
TheDeeno
No, your entities do not necessarily need to be DTOs only. I was merely saying that an interface shouldn't be needed if there is not an aspect of change to the AccountManager, don't use an interface for the sake of using an interface. If in your case the logic does change then you can easily use a base-class instead of an interface in conjunction with a discriminator, NHibernate will then be able to instantiate the correct subclass when retrieving/saving the entity. Barring a discriminator there is no one-to-one mapping for an interface to a type, because then what's the point of an interface?
joshperry
+4  A: 

I did find an answer to this one. I'm a little hazy on the details, as it was a few months ago, but the following was the jist of the solution:

  • Create a table for each implementation of IAccountManager that has mappings.
  • Make sure your DB is setup to use the HiLo id algorithm.
  • Use union-subclasses in your mappings

Union-subclasses would look something like this:

<class name="IAccountManager" abstract="true">
  <id name="ID" column="ID" type="Int32">
    <generator class="hilo"/>
  </id>
  <union-subclass name="DefaultAccountManager" table="DefaultAccountManager"
      proxy="IAccountManager">
    <property name="FirstName" type="String"/>
    <property name="LastName" type="String"/>
  </union-subclass>
  ... more implementations
</class>

Note the attribute "name" on union-subclass. This should be unique for (and match) each implementation of IAccountManager.

Also, the ID, instead of being unique to each table, will be unique to all IAccountManagers (by leveraging hilo).

When NHibernate sees an IAccountManager entity, it will use the instance's concrete type and the union-subclass definitions to figure out the correct table.

Hope this helps!

TheDeeno
+1  A: 

Just thought I would share a way that I managed to achieve this using Fluent NHibernate rather than the hbm files.

This method is a little hacky but the hacks are isolated and easily removed once Fluent NH gains proper support for Union-Subclass.

To use your example, the context of my scenario is this - the Employee class is in one project with the AccountManager property specified as an interface, because the concrete AccountManager is in a different project which we don't want to create a dependency to.

First I create a 'Helper' class that does most of the Employee mapping and looks like this.

public abstract class EmployeeMapperBase
{
    protected abstract Type GetAccountManagerType();

    public void DoMapping(ClassMap<Employee> classMap)
    {
        classMap.Id(x => x.Id);

        classMap.Maps(..... etc....

        classMap.References(x => x.AccountManager)
            .Class(GetAccountManagerType());
    }
}

Next, in the project with the concrete AccountManager class, I complete the mapping:

public class EmployeeClassMap : ClassMap<Employee>
{
    public EmployeeClassMap
    {
        new ConcreteEmployeeMapper().DoMapping(this);
    }

    private class ConcreteEmployeeMapper : EmployeeMapperBase
    {
        public override Type GetAccountManagerType()
        {
            return typeof(DefaultAccountManager);
        }
    }
}
cbp
A little hacky, yes, but it seems like a helpful work around for those who want to really stay away from xml mappings. Personally, the xml mappings clean and completely usable for me. Others might not agree. Thanks for sharing.
TheDeeno