NHibernate Mapping - 正在重用列的UserType?

时间:2011-03-15 18:17:44

标签: c# nhibernate orm nhibernate-mapping usertype

我基本上有一个Money值类型,它由AmountCurrency组成。我需要将多个Money值映射到一个表中,该表有多个金额字段,但只有一个货币。换句话说,我有桌子:

Currency     Amount1    Amount2
===============================
USD          20.00      45.00

哪个需要映射到具有2个Money值的类(逻辑上从不具有不同的货币):

class Record
{ 
    public Money Value1 { get; set; }
    public Money Value2 { get; set; }
}

struct Money 
{
    public decimal Amount { get; set; }
    public string Currency { get;set; }
} 

(示例有点简化)

无法更改表架构。如果需要,我很乐意实施IUserType,但我无法弄清楚如何访问这两个值的Currency列。

如何使用NHibernate进行映射?

5 个答案:

答案 0 :(得分:2)

这是不可能的。

如果创建Money UserType,则类型的目的是将两个基元组合成新的单一数据类型。该数据类型现在是单个原子单元。由于UserType被视为原子类型,因此映射的每个Money值都必须存在两列。 NHibernate强制实施的规则实际上在语义上是正确的。你有两个Money值。因此,您必须拥有两种货币 - 它们不能共享一种货币,因为单个货币类型具有货币和金额,现在是一个原子数据单位,不能拆分或共享。

您希望有时将货币视为变量,因此需要自己的列,并且在其他时候将其视为已修复,以便多个UserType可以共享一列。即使这是有效的,它不是基于如何定义Money,NHibernate将如何知道你想做什么?系统无法在任何给定时间知道您想要什么。您现在还需要使用Money类型保存其他数据,该类型指定了如何使用任何给定值。

底线,您的列不能映射为UserType。如果您无法更改架构,则可以执行此操作的唯一方法是将所有三列映射为普通基元,然后在应用程序代码中构造(并解构)Money类型。

结束了什么?我真的很惊讶甚至像Fowler这样的人也认为这种方法是某种“最佳实践”而没有真正考虑实际的细节。事实上,大多数数据都是一种货币由一些经常隐含或外部因素决定的行,例如原产国或企业经营的国家等等。

在您实际上甚至需要货币的情况下,您经常会有太多其他行李,这些行李来自多种货币,例如当前的汇率等,其中货币作为货币类型的一部分并不是那么有用。这是一种方便,但是对于非常不方便使用的数据而言是不可能的。大部分时间货币是固定的,通常甚至可以推断出来。因此,在典型情况下,Money类型无法接近您真正需要的 - 转换,或者它只是不必要的信息。除了少数例外,Money类型在应用程序的帮助下变成了一个dingleberry。

在你花费大量时间尝试实现某些东西之前,可能很少或没有其他原因,人们已经开始称之为“最佳实践”,问问自己,你是否甚至需要将它用于任何事情? / p>

答案 1 :(得分:1)

这是不可能的,因为当您考虑读写实体时它没有意义。

考虑Value1为USD 20且Value2为GBP 40的情况。你会如何坚持下去?

答案 2 :(得分:1)

如何改变这样的课程呢?

class Record
{ 
    public MoneyCollection Money { get; set; }
}

class MoneyCollection
{
    public MoneyCollection(string currency, params decimal[] amount1) { /*...*/ }
    public decimal[] Amount { get; private set; }
    public string Currency { get; private set; }
    public Money[] Money
    {
      get
      {
        return Amount.Select(x => new Money(Currency, x)).ToArray();
      }
    }
} 

class Money 
{
    public Money(decimal amount, string currency ) { /* ... */ }
    public decimal Amount { get; private set; }
    public string Currency { get; private set; }
}

现在您可以为MoneyCollection编写用户类型。

注意:您需要确保MoneyCollection具有常数或至少最大数量的值,因为您需要将其映射到固定数量的列。在班级中检查这一点。

答案 3 :(得分:0)

我只是一个刚刚学习和使用NHibernate几个月的新手,但是不可能制作一个写入多列的复杂usertype(IEnhancedUserType),而一列是多余的? / p>

很难解释,但如果您将实体中的属性映射到负责读/写操作的货币列,并且您在复杂的usertype映射中多次引用同一列并关闭插入和更新,从而使它成为一个只读列,不会有用吗?我一直在考虑尝试使用这样的usertype来查看它是否有效(因为我还没有尝试过,我没有任何示例代码atm)

答案 4 :(得分:0)

我知道这已经晚了几年 - 但我有一个解决方案。

private decimal _subTotal;
private decimal _shipping;
private CurrencyIsoCode _currency;

public virtual Money SubTotal
{
    get { return new Money(_subTotal, _currency); }
    set
    {
        _subTotal = value.Amount;
        _currency = value.CurrencyCode;
    }
}

public virtual Money Shipping
{
    get { return new Money(_shipping, _currency); }
    set
    {
        _shipping = value.Amount;
        _currency = value.CurrencyCode;
    }
}

<强>映射:

Map(Reveal.Member<Basket>("_subTotal")).Column("SubTotal");
Map(Reveal.Member<Basket>("_shipping")).Column("Shipping");
Map(Reveal.Member<Basket>("_currency")).Column("Currency");