【问题标题】:NHibernate Mapping - UserType with a column being re-used?NHibernate 映射 - 带有被重用列的用户类型?
【发布时间】:2011-03-15 18:17:44
【问题描述】:

我基本上有一个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 进行映射?

【问题讨论】:

    标签: c# nhibernate orm nhibernate-mapping usertype


    【解决方案1】:

    这是不可能的。

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

    您有时希望将货币视为变量,因此需要它自己的列,而在其他时候,您希望将货币视为固定的,以便多个 UserType 可以共享一个列。即使这是有效的,它不是基于金钱的定义方式,NHibernate 怎么会知道你什么时候想做什么?系统不能在任何给定时间只知道您想要什么。您现在还需要使用 Money 类型保存其他数据,以指定任何给定值的使用方式。

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

    结果是什么?我真的很惊讶,即使是像 Fowler 这样的人也建议这种方法作为某种“最佳实践”而没有真正考虑实际细节。事实上,大多数数据是一组货币由一些通常隐含或外部因素(例如原产国或企业经营所在的国家/地区等)决定的。

    在您实际上甚至可能需要货币的情况下,您经常有很多其他的包袱,这些包袱来自多种货币,例如当前汇率等,以至于将货币作为货币类型的一部分甚至没有那么有用。这是一种方便,但对于使用起来非常不方便且无法以其他方式制作的数据。大多数情况下,货币是固定的,通常甚至可以推断出来。因此,在典型情况下,Money 类型要么无法接近您真正需要的 - 转换,要么只是不必要的信息。除了少数例外,Money 类型只是应用程序中的一个小果子。

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

    【讨论】:

    • +1,虽然我不同意你关于 Money 值类型的有用性。
    【解决方案2】:

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

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

    【讨论】:

    • 我明白这一点,但我的逻辑表明,同一记录中的货币必须相同。如果我可以更改架构以包含多个货币列,我可能会。
    • @driis:那么您需要更改您的域。此外,NHibernate 不可能知道该约束。
    【解决方案3】:

    像这样改变你的课程怎么样?

    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 有一个常数,或者至少有一个最大数量的值,因为您需要将它映射到一个常数列。在类本身中检查这一点。

    【讨论】:

      【解决方案4】:

      我只是一个新手,现在才学习和使用 NHibernate 几个月,但是是否可以创建一个复杂的用户类型 (IEnhancedUserType) 来写入多个列,而一个列将是多余的?

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

      【讨论】:

        【解决方案5】:

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

        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");
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-12-09
          • 2010-11-05
          • 1970-01-01
          • 2011-08-18
          • 2013-03-16
          相关资源
          最近更新 更多