NHibernate 映射 - 带有被重用列的用户类型?
Posted
技术标签:
【中文标题】NHibernate 映射 - 带有被重用列的用户类型?【英文标题】:NHibernate Mapping - UserType with a column being re-used? 【发布时间】:2011-03-15 18:17:44 【问题描述】:我基本上有一个Money
值类型,它由Amount
和Currency
组成。我需要将多个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 进行映射?
【问题讨论】:
【参考方案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");
【讨论】:
以上是关于NHibernate 映射 - 带有被重用列的用户类型?的主要内容,如果未能解决你的问题,请参考以下文章