Fluent nhibernate: How do I map an entity with a p

2019-04-01 10:29发布

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.

3条回答
男人必须洒脱
2楼-- · 2019-04-01 10:53

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!

查看更多
兄弟一词,经得起流年.
3楼-- · 2019-04-01 11:00

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.

查看更多
对你真心纯属浪费
4楼-- · 2019-04-01 11:12

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);
        }
    }
}
查看更多
登录 后发表回答