简体   繁体   English

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

[英]Mapping the same entity to different tables

A bit of domain knowledge一点领域知识

I'm writing a POS (Point Of Sales) software which allows to pay goods or to refund them.我正在编写一个 POS(销售点)软件,它允许支付货物或退款。 When paying or refunding, one need to specify which money transfer mean to use: cash, EFT (~=credit card), loyalty card, voucher, etc.在付款或退款时,需要指定使用哪种汇款方式:现金、EFT(~=信用卡)、会员卡、代金券等。

These money transfer means are a finite and known set of values (a kind of enum).这些汇款方式是一组有限且已知的值(一种枚举)。

The tricky part is that I need to be able to store a custom subset of these means for both payments and refunds (the two sets may be different) on the POS terminal.棘手的部分是我需要能够在 POS 终端上存储这些方式的自定义子集,用于付款和退款(两组可能不同)。

For example:例如:

  • Available payment means: Cash, EFT, Loyalty card, Voucher可用的付款方式:现金、电子转帐、会员卡、代金券
  • Available refund means: Cash, Voucher可用退款方式:现金、代金券

Current state of implementation实施现状

I choose to implement the concept of money transfer mean as follow:我选择实现汇款方式的概念如下:

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

The reason it is not a "plain enum" is that there exists some behavior that is inside these classes.它不是“普通枚举”的原因是这些类内部存在一些行为。 I also had to declare inner classes public (instead of private) in order to reference them in FluentNHibernate mapping (see below).我还必须将内部类声明为 public(而不是私有),以便在 FluentNHibernate 映射中引用它们(见下文)。

How it is used如何使用

Both the payment and refund means are always stored or retrieved in/from the DB as a set.支付和退款手段总是作为一个集合存储在/从数据库中检索。 They are really two distinct sets even though some values inside both sets may be the same.它们实际上是两个不同的集合,即使两个集合中的某些值可能相同。

Use case 1: define a new set of payment/refund means用例 1:定义一组新的支付/退款方式

  • Delete all the existing payment/refund means删除所有现有的支付/退款方式
  • Insert the new ones插入新的

Use case 2: retrieve all the payment/refund means用例2:检索所有支付/退款方式

  • Get a collection of all the stored payment/refund means获取所有存储的付款/退款方式的集合

Problem问题

I'm stuck with my current design on the persistence aspect.我在持久性方面坚持我目前的设计。 I'm using NHibernate (with FluentNHibernate to declare class maps) and I can't find a way to map it to some valid DB schema.我正在使用 NHibernate(使用 FluentNHibernate 来声明类映射),但找不到将其映射到某些有效数据库模式的方法。

I found that it is possible to map a class multiple times using entity-name however I'm not sure that it is possible with subclasses.我发现可以使用entity-name多次映射一个类,但是我不确定子类是否可行。

What I'm not ready to do is to alter the MoneyTransferMean public API to be able to persist it (for example adding a bool isRefund to differentiate between the two).我还没有准备好改变 MoneyTransferMean 公共 API 以使其能够持久化(例如添加bool isRefund以区分两者)。 However adding some private discriminator field or so is ok.但是,添加一些私有鉴别器字段左右是可以的。

My current mapping:我目前的映射:

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

This mapping compiles however it only produces 1 table and I'm not able to differentiate between payment/refund when querying this table.这个映射编译但是它只生成 1 个表,在查询这个表时我无法区分付款/退款。

I tried to declare two mappings referencing both MoneyTransferMean with different table and entity-name however this leads me to an exception Duplicate class/entity mapping MoneyTransferMean+CashMoneyTransferMean .我试图声明两个映射,引用具有不同表和实体名称的MoneyTransferMean但这导致我出现异常Duplicate class/entity mapping MoneyTransferMean+CashMoneyTransferMean

I also tried to duplicate subclass mappings but I'm unable to specify a "parent mapping" which leads me to the same exception as above.我还尝试复制子类映射,但我无法指定“父映射”,这导致我遇到与上述相同的异常。

Question

Does a solution exist to persist my current domain entities ?是否存在保留我当前域实体的解决方案?

If not, what would be the smallest refactor I need to perform on my entities to make them persistable with NHibnernate ?如果不是,我需要对我的实体执行的最小重构是什么,以使它们与 NHibnernate 保持一致?

Why don't you create one single entity MoneyTransferMean , with all the common properties (fields) and just add 2 extra fields (boolean) to determine if that MoneyTransferMean is either Payment or Refund, or both????为什么不创建一个具有所有通用属性(字段)的单一实体MoneyTransferMean ,然后添加 2 个额外字段(布尔值)来确定 MoneyTransferMean 是 Payment 还是 Refund,还是两者兼而有之???? Persist it or not.坚持与否。

Also it can be done with a extra Entity with Id (PK), add same extra fields, relationship would be 1:1 with MoneyTransferMean.也可以使用带有 Id (PK) 的额外实体来完成,添加相同的额外字段,与 MoneyTransferMean 的关系将是 1:1。 Ugly, I know, but it should work.丑陋,我知道,但它应该工作。

I'd second and add to what @DEVX75 suggested, in that your transaction types are essentially describing the same concept, though one is +ve whilst the other is -ve.我想补充一下@DEVX75 的建议,因为您的交易类型基本上描述了相同的概念,尽管一个是 +ve 而另一个是 -ve。 I'd probably add just one boolean field though, and have separate records to discern refunds from payments.不过,我可能只添加一个布尔字段,并有单独的记录来区分退款和付款。

Assuming you have a UID and are not using the means label name as the ID, you can allow duplicate names for means, and include two cash entries, for example:假设您有一个 UID 并且没有使用手段标签名称作为 ID,您可以允许手段重复名称,并包括两个现金条目,例如:

UID, Label, IsRefund UID、标签、IsRefund

1, Cash, false 1、现金、假

2, Cash, true 2、现金,真实

3, Voucher, false 3、代金券,假的

4, Voucher, true 4、代金券,真实

Then you can easily get the following:然后您可以轻松获得以下内容:

Transaction Type = MoneyTransferMean.IsRefund?交易类型= MoneyTransferMean.IsRefund? "Refund" : "Payment" “退款”:“付款”

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

That way, if in your transactions you've referenced MoneyTransferMean.UID = 2, you know that that is a cash refund, rather than knowing that that is a transaction type that could be either a cash refund or a cash payment.这样,如果您在交易中引用了 MoneyTransferMean.UID = 2,您就知道这是现金退款,而不是知道这是一种可以是现金退款或现金支付的交易类型。

Finally, I decided to solve the problem by duplicating my entity MoneyTransferMean into two entities PaymentMean and RefundMean .最后,我决定通过将我的实体MoneyTransferMean复制到两个实体PaymentMeanRefundMean来解决这个问题。

Although similar in implementation, the distinction between the two entities makes sense in the business and was for me the least worst solution.虽然在实现上相似,但两个实体之间的区别在业务中是有意义的,对我来说是最不糟糕的解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM