【问题标题】:Mapping the same entity to different tables将同一实体映射到不同的表
【发布时间】:2019-12-20 07:54:02
【问题描述】:

一点领域知识

我正在编写一个 POS(销售点)软件,它允许付款或退款。 付款或退款时,需要指定使用哪种汇款方式:现金、电子转帐(~=信用卡)、会员卡、代金券等。

这些汇款方式是一组有限且已知的值(一种枚举)。

棘手的部分是我需要能够在 POS 终端上存储这些方式的自定义子集,用于支付和退款(这两组可能不同)。

例如:

  • 可用的支付方式:现金、电子转帐、忠诚卡、优惠券
  • 可用退款方式:现金、代金券

目前的实施状态

我选择实现汇款方式的概念如下:

public abstract class MoneyTransferMean : AggregateRoot
{
    public static readonly MoneyTransferMean Cash = new CashMoneyTransferMean();
    public static readonly MoneyTransferMean EFT = new EFTMoneyTransferMean();
    // and so on...

    //abstract method

    public class CashMoneyTransferMean : MoneyTransferMean
    {
        //impl of abstract method
    }

    public class EFTMoneyTransferMean : MoneyTransferMean
    {
        //impl of abstract method
    }

    //and so on...
}

它不是“普通枚举”的原因是这些类中存在一些行为。为了在 FluentNHibernate 映射中引用它们,我还必须将内部类声明为公共(而不是私有)(见下文)。

如何使用

付款和退款方式始终作为一组在数据库中存储或检索。它们实际上是两个不同的集合,即使两个集合中的某些值可能相同。

用例 1:定义一组新的付款/退款方式

  • 删除所有现有的付款/退款方式
  • 插入新的

用例 2:检索所有付款/退款方式

  • 获取所有存储的付款/退款方式的集合

问题

我在持久性方面坚持我目前的设计。我正在使用 NHibernate(使用 FluentNHibernate 来声明类映射),但我找不到将其映射到某些有效数据库模式的方法。

我发现可以使用entity-name 多次映射一个类,但我不确定子类是否可行。

我还没准备好更改 MoneyTransferMean 公共 API 以使其能够持久化(例如添加 bool isRefund 以区分两者)。但是添加一些私有鉴别器字段左右是可以的。

我当前的映射:

public sealed class MoneyTransferMeanMap : ClassMap<MoneyTransferMean>
{
    public MoneyTransferMeanMap()
    {
        Id(Entity.Expressions<MoneyTransferMean>.Id);
        DiscriminateSubClassesOnColumn("Type")
            .Not.Nullable();
    }
}

public sealed class CashMoneyTransferMeanMap : SubclassMap<MoneyTransferMean.CashMoneyTransferMean>
{
    public CashMoneyTransferMeanMap()
    {
        DiscriminatorValue("Cash");
    }
}

public sealed class EFTMoneyTransferMeanMap : SubclassMap<MoneyTransferMean.EFTMoneyTransferMean>
{
    public EFTMoneyTransferMeanMap()
    {
        DiscriminatorValue("EFT");
    }
}

//and so on...

此映射可以编译,但它只生成 1 个表,在查询此表时我无法区分付款/退款。

我尝试使用不同的表和实体名称声明两个引用 MoneyTransferMean 的映射,但这导致我出现异常 Duplicate class/entity mapping MoneyTransferMean+CashMoneyTransferMean

我也尝试复制子类映射,但我无法指定“父映射”,这导致我遇到与上述相同的异常。

问题

是否存在保留我当前域实体的解决方案?

如果不是,我需要对我的实体执行的最小重构是什么,以使它们可以通过 NHibnernate 持久化?

【问题讨论】:

  • 为什么要将这些支付方式存储在数据库中?除了名字之外,那里的状态是什么?
  • @berhalak 实际上,将这些存储在数据库中看起来有点笨拙。然而,项目要求所有状态都在数据库中。

标签: c# nhibernate fluent-nhibernate domain-driven-design fluent-nhibernate-mapping


【解决方案1】:

为什么不创建一个具有所有公共属性(字段)的单一实体 MoneyTransferMean,然后只需添加 2 个额外字段(布尔值)来确定 MoneyTransferMean 是 Payment 还是 Refund,或者两个都????坚持与否。

也可以使用带有 Id (PK) 的额外实体来完成,添加相同的额外字段,与 MoneyTransferMean 的关系将是 1:1。丑陋,我知道,但它应该可以工作。

【讨论】:

  • 我不想在我的域项目中添加与域无关的复杂性(例如添加需要后续 if/else 的布尔值)。另外,我不希望使用这些类的人犯错误(例如忘记检查布尔值并认为每个值都是退款的意思)。一切都是为了明确。
【解决方案2】:

我想补充一下@DEVX75 的建议,因为您的事务类型基本上描述了相同的概念,尽管一个是 +ve 而另一个是 -ve。不过,我可能只添加一个布尔字段,并有单独的记录来区分退款和付款。

假设您有一个 UID 并且没有使用手段标签名称作为 ID,您可以允许手段名称重复,并包括两个现金条目,例如:

UID、标签、IsRefund

1,现金,假

2,现金,真实

3,凭证,假

4,凭证,真实

那么你就可以轻松搞定了:

交易类型 = MoneyTransferMean.IsRefund? “退款”:“付款”

交易价值 = MoneyTransferMean.IsRefund? MoneyTransfer.amount * -1 : MoneyTransfer.amount

这样,如果您在交易中引用了 MoneyTransferMean.UID = 2,则您知道这是现金退款,而不是知道这是一种可以是现金退款或现金付款的交易类型。

【讨论】:

  • 啊混蛋,刚刚注意到你说你不想/不能编辑公共 API。抱歉/忽略我的回答,尽管我会留下它,因为它可能对其他有类似问题/用例的人有用。
【解决方案3】:

最后,我决定通过将我的实体 MoneyTransferMean 复制成两个实体 PaymentMeanRefundMean 来解决这个问题。

虽然在实现上相似,但这两个实体之间的区别在业务中是有意义的,对我来说是最糟糕的解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-04-08
    • 2010-10-07
    • 2020-06-09
    • 1970-01-01
    • 1970-01-01
    • 2019-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多