将同一个实体映射到不同的表

Mapping the same entity to different tables

一点领域知识

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

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

棘手的部分是我需要能够在 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...
}

它不是 "plain enum" 的原因是这些 class 中存在一些行为。我还必须声明内部 classes public(而不是私有的)以便在 FluentNHibernate 映射中引用它们(见下文)。

如何使用

付款方式和退款方式总是作为一组存储或检索 in/from 数据库。它们实际上是两个不同的集合,尽管两个集合中的某些值可能相同。

用例1:定义一组新的payment/refund意味着

用例 2:检索所有 payment/refund 手段

问题

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

我发现可以使用 entity-name 多次映射 class 但是我不确定是否可以使用 subclasses.

我还没准备好做的是改变 MoneyTransferMean public 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 table 并且在查询这个 table.

时我无法区分 payment/refund

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

我也尝试复制 subclass 映射,但我无法指定 "parent mapping",这导致我遇到与上述相同的异常。

问题

是否存在持久化我当前域实体的解决方案?

如果不是,我需要对我的实体执行什么最小的重构才能使它们与 NHibnernate 保持一致table?

你为什么不创建一个单一的实体 MoneyTransferMean,具有所有公共属性(字段)并添加 2 个额外的字段(布尔值)以确定 MoneyTransferMean 是否是付款或退款,或两者兼而有之????坚持不坚持。

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

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

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

UID, Label, IsRefund

1, Cash, false

2, Cash, true

3, Voucher, false

4, Voucher, true

那么你可以轻松得到以下内容:

Transaction Type = MoneyTransferMean.IsRefund? "Refund" : "Payment"

Transaction Value = MoneyTransferMean.IsRefund? MoneyTransfer.amount * -1 : MoneyTransfer.amount

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

最后,我决定通过将实体 MoneyTransferMean 复制为两个实体 PaymentMeanRefundMean.

来解决问题

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