【问题标题】:Nhibernate inheritance mapping mixing Type-Per-Hierarchy with Type-Per-Class?Nhibernate继承映射混合Type-Per-Hierarchy和Type-Per-Class?
【发布时间】:2016-01-11 04:03:09
【问题描述】:

我正在尝试映射这些类:

public interface IBusinessObject
{
    Guid Id { get; set; }
}
public class Product
{
    public virtual Guid Id { get; set; }
    public virtual int ProductTypeId { get; set; }
}
public class ProductWeSell : Product, IBusinessObject
{
}
public class ProductWeDontSell : Product
{
}

到有 2 个表的数据库:

[BusinessObject] COLUMNS ([Id])
[Product] COLUMNS ([Id], [ProdyctTypeId])

我想对 BusinessObject 使用 Type-Per-Class,对 Product 使用 Type-Per-Hierarchy。这应该会导致这种行为:

  • 添加产品:插入产品 {Guid),null}
  • 添加 ProductWeDontSell:插入产品 {Guid, 2}
  • 添加 ProductWeSell:INSERT INTO BusinessObject {Guid};插入产品 {SameGuid, 1}

逻辑上 hbm 映射应该是:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class xmlns="urn:nhibernate-mapping-2.2" name="IBusinessObject" table="BusinessObject">
    <joined-subclass name="ProductWeSell" table="Product"/>
  </class>
</hibernate-mapping>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class xmlns="urn:nhibernate-mapping-2.2" discriminator-value="null" name="Product" table="Product">
    <discriminator type="String">
      <column name="ProductTypeId" not-null="false" />
    </discriminator>
    <subclass name="ProductWeDontSell" discriminator-value="2" />
    <subclass name="ProductWeSell" discriminator-value="1" />
  </class>
</hibernate-mapping>

但我收到 {"Duplicate class/entity mapping ProductWeSell"} 错误。

【问题讨论】:

标签: c# nhibernate nhibernate-mapping


【解决方案1】:

我相当肯定 Fluent NHibernate 无法做到这一点。当我试图想出一种方法时,我创建了以下 TestFixture。另外,我不确定您要映射什么。如果您要将 IBusinessObject 用于系统中的每个实体,它似乎是多余的(CreateQuery("from object") 将返回从对象继承的每个实体,即一切)

注意:目前这在 IBusinessObject 查询上失败(因为它应该没有映射)。我不太确定为什么如果不映射 IBusinessObject 接口就不能工作,因为隐式多态性似乎应该涵盖这一点(参见:http://nhibernate.info/doc/nh/en/index.html#inheritace-mixingpolymorphism)。

[TestFixture]
public class TestFixture
{

    private ISessionFactory _sessionFactory;
    private ISession _session;
    [SetUp]
    public void Setup()
    {
        var fluentConfig = Fluently.Configure().Database(() => SQLiteConfiguration.Standard.InMemory().Provider<TestConnectionProvider>())
                                   .Mappings(x=>x.FluentMappings.Add<ProductMap>()
                                                                .Add<ProductWeSellMap>()
                                                                .Add<ProductWeDontSellMap>());
        var nhConfig = fluentConfig.BuildConfiguration();
        _sessionFactory = fluentConfig.BuildSessionFactory();

        var schema = new SchemaExport(nhConfig);
        schema.Execute(false, true, false);

        _session = _sessionFactory.OpenSession();
    }

    [Test]
    public void SomeTest()
    {
        using (var itx = this._session.BeginTransaction())
        {
            var productSold = new ProductWeSell();
            var productNotSold = new ProductWeDontSell();
            _session.Save(productNotSold);
            _session.Save(productSold);
            itx.Commit();
        }

        using (var itx = this._session.BeginTransaction())
        {
            Assert.That(_session.CreateQuery("from ProductWeSell").List(), Has.Count.EqualTo(1));
            Assert.That(_session.CreateQuery("from ProductWeDontSell").List(), Has.Count.EqualTo(1));
            Assert.That(_session.CreateQuery("from IBusinessObject").List(), Has.Count.EqualTo(1));
            Assert.That(_session.CreateQuery("from Product").List(), Has.Count.EqualTo(2));
        }
    }
}

public interface IBusinessObject
{
    Guid Id { get; set; }
}

public class Product
{
    public virtual Guid Id { get; set; }
    public virtual int ProductTypeId { get; set; }
}
public class ProductMap : ClassMap<Product>
{
    public ProductMap()
    {
        this.Id(x => x.Id);
        this.DiscriminateSubClassesOnColumn("ProductTypeId");
    }
}

public class ProductWeSell : Product, IBusinessObject
{
    public const int ProductWeSellTypeId = 1;
}

public class ProductWeSellMap : SubclassMap<ProductWeSell>
{
    public ProductWeSellMap()
    {
        this.DiscriminatorValue(ProductWeSell.ProductWeSellTypeId);
    }
}

public class ProductWeDontSell : Product
{
    public const int ProductWeDontSellTypeId = 2;
}

public class ProductWeDontSellMap : SubclassMap<ProductWeDontSell>
{
    public ProductWeDontSellMap()
    {
        this.DiscriminatorValue(ProductWeDontSell.ProductWeDontSellTypeId);
    }
}

这会产生以下 HBM 映射:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="CellTester.Test.Database.Product, Test, Version=2.0.0.3, Culture=neutral, PublicKeyToken=a15dc1b99998d28b" table="`Product`">
    <id name="Id" type="System.Guid, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Id" />
      <generator class="guid.comb" />
    </id>
    <discriminator type="String">
      <column name="ProductTypeId" />
    </discriminator>
    <subclass name="ProductWeSell, Test, Version=2.0.0.3, Culture=neutral, PublicKeyToken=a15dc1b99998d28b" discriminator-value="1" />
    <subclass name="ProductWeDontSell, Test, Version=2.0.0.3, Culture=neutral, PublicKeyToken=a15dc1b99998d28b" discriminator-value="2" />
  </class>
</hibernate-mapping>

【讨论】:

  • @Nicholas : 引用“如果您要将 IBusinessObject 用于系统中的每个实体,它似乎是多余的”。我不会与每个实体一起使用,这正是我需要它的原因。看看 ProductWeDontSell : Product - 它是产品,但不是 IBusinessObject。
  • @Broken Pipe:好的。你想用 IBusinessObject 做什么?为什么会在那里? (我知道你已经创建了一个玩具示例来发布)你想如何在查询中使用它?现在你可以只做“来自 ProductWeSell”,它相当于(现在不能工作“来自 IBusinessObject”)。
  • @Nicholas:我打算使用 IBusinessObject 来获取所有实现此接口的产品。或者更深层次的想法是,用它来标记一些对象本身很重要,而一些只是其他更大对象图的一部分。我想使用 I BusinessObject 作为聚合根(对象图根)的指针。等等,我会尝试更好地解释这一点)
  • @Broken Pipe:为每个根使用抽象基类比使用接口要容易得多。从我所做的所有谷歌搜索中,我试图找出这是否可能,似乎 A)FNH 的作者并不真正喜欢这种实现(参见:tinyurl.com/5tf6lxc)和 B)FNH 似乎缺乏支持的接口。您可以使用 HBM 解决此问题,或者尝试修复 FNH(围绕 FNH.Visitors.SeparateSubclassVisitor.DistanceFromParentBase)
  • @Nicholas:我同意,使用抽象类会更容易,但我需要多重继承,而 C# 只支持多重接口继承。我真的想让我所有的“产品”都派生自 Product 类,其中一些派生自其他类(接口)。在数据库级别很容易做到,而且我有工作数据库,我可以根据需要使用普通 SQL 轻松查询它,但无法将其映射到 Nhibernate。此外,我刚刚在 NH 邮件列表上看到您无法通过接口查询,不确定是否属实。
【解决方案2】:

我得出了一个有趣的结论。我决定改变我的架构并为数据访问层和域 UI 层使用单独的类,我只是在我的对象跨层时转换它们。无论如何,将数据访问和 UI 解耦是一个好习惯。所以我的数据访问模型现在很简单,没有任何继承,继承只出现在域 UI 级别。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-29
    • 1970-01-01
    • 2019-12-28
    • 1970-01-01
    相关资源
    最近更新 更多