【问题标题】:Fluent nhibernate: How do I map an entity with a property who's type is an interface?Fluent nhibernate:如何将实体与类型为接口的属性映射?
【发布时间】:2010-10-25 09:08:14
【问题描述】:

我有一个像这样的实体:

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

我还为“DefaultAccountManager”定义了一个映射 - IAccountManager 的具体实现。在映射上述“Employee”实体时,我如何告诉 NHibernate 使用“DefaultAccountManager”中定义的映射来持久/加载 AccountManager 属性?

编辑: 实际上,如果我可以为 IAccountManager 设置一个映射,以便 NHibernate 可以推断出哪个实现者来加载/持久化,那就更好了。我宁愿不必通过强制所有实现者使用相同的映射来破坏多态性。

【问题讨论】:

  • 你有没有找到解决这个问题的方法?
  • 啊,忘了这个了。将在下面解释我的答案。

标签: c# nhibernate fluent-nhibernate interface nhibernate-mapping


【解决方案1】:

如果您需要多态性,因为 IAccountManager 实现的功能可能具有不同的功能,您可以查看 discriminators 并使用基类而不是接口。

如果您只是使用接口,因为它们是当前的 OOP 范式,那么请三思而后行,因为实体通常几乎没有行为,在这种情况下,接口提供的价值很少(如果有的话)。

【讨论】:

  • 我最近几次听到实体的接口=“无价值”响应。 NHibernate 是否期望我的实体只是 DTO 而不是其他逻辑?对 NHibernate 还不是很满意,所以我只是好奇。
  • 不,您的实体不一定只需要是 DTO。我只是说如果没有对 AccountManager 进行更改的方面,则不需要接口,不要为了使用接口而使用接口。如果在您的情况下逻辑确实发生了变化,那么您可以轻松地将基类而不是接口与鉴别器结合使用,NHibernate 将能够在检索/保存实体时实例化正确的子类。除非有鉴别器,否则接口与类型之间没有一对一的映射,因为那么接口的意义何在?
  • @joshperry。谢谢你。我遇到了与原始海报几乎相似的问题,并且正试图完全按照您说的不要做,我对此感觉不对,但不确定。你的解释帮助我摆脱了它。
【解决方案2】:

我确实找到了这个问题的答案。我对细节有点模糊,就像几个月前一样,但以下是解决方案的要点:

  • 为每个具有映射的 IAccountManager 实现创建一个表。
  • 确保您的数据库设置为使用 HiLo id 算法。
  • 在映射中使用联合子类

联合子类看起来像这样:

<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>

注意 union-subclass 的属性“name”。这对于 IAccountManager 的每个实现应该是唯一的(并且匹配)。

此外,ID 不是每个表唯一的,而是所有 IAccountManager 唯一的(通过利用 hilo)。

当 NHibernate 看到 IAccountManager 实体时,它将使用实例的具体类型和联合子类定义来找出正确的表。

希望这会有所帮助!

【讨论】:

    【解决方案3】:

    只是想我会分享一种使用 Fluent NHibernate 而不是 hbm 文件来实现这一目标的方法。

    这种方法有点hacky,但是一旦Fluent NH 获得对Union-Subclass 的适当支持,这些hack 就会被隔离并轻松删除。

    使用您的示例,我的场景的上下文是这样的 - Employee 类位于一个项目中,其中 AccountManager 属性指定为接口,因为具体的 AccountManager 位于另一个我们不想创建的项目中依赖于。

    首先,我创建了一个“Helper”类,它执行大部分 Employee 映射,看起来像这样。

    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());
        }
    }
    

    接下来,在具有具体 AccountManager 类的项目中,我完成了映射:

    public class EmployeeClassMap : ClassMap<Employee>
    {
        public EmployeeClassMap
        {
            new ConcreteEmployeeMapper().DoMapping(this);
        }
    
        private class ConcreteEmployeeMapper : EmployeeMapperBase
        {
            public override Type GetAccountManagerType()
            {
                return typeof(DefaultAccountManager);
            }
        }
    }
    

    【讨论】:

    • 有点 hacky,是的,但对于那些想要真正远离 xml 映射的人来说,这似乎是一个有用的解决方法。就个人而言,xml 映射对我来说很干净并且完全可用。其他人可能不同意。感谢分享。
    猜你喜欢
    • 2013-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-28
    • 1970-01-01
    • 2015-06-20
    • 2015-05-23
    • 2012-02-18
    相关资源
    最近更新 更多