Pregunta

Básicamente tengo un Money tipo de valor, que consiste en Amount y Currency. Necesito mapear múltiples Money valores en una tabla, que tiene múltiples campos para la cantidad, pero solo una moneda. En palabras, tengo la tabla:

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

Que debe asignarse a una clase con 2 valores de dinero (que lógicamente nunca puede tener diferentes monedas):

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

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

(El ejemplo es un poco simplificado)

El esquema de la tabla no se puede cambiar. Estoy feliz de implementar un IUserType, si es necesario, pero no puedo entender cómo acceder al Currency columna para ambos valores.

¿Cómo mapeo esto usando NHibernate?

¿Fue útil?

Solución

No es posible.

Si crea un dinero de dinero, el propósito del tipo es combinar dos primitivas en un nuevo tipo de datos individuales. Ese tipo de datos ahora es una sola unidad atómica. Debido a que el UserType se considera un tipo atómico, ambas columnas deben estar presentes para cada valor de dinero asignado. La regla que está haciendo cumplir es, de hecho, semánticamente correcta. Tienes dos valores de dinero. Por lo tanto, debe tener dos monedas: no pueden compartir una porque un solo tipo de dinero tiene una moneda y cantidad y esa es una unidad de datos atómicos ahora, no se puede dividir o compartir.

A veces desea tratar la moneda como variable y, por lo tanto, necesita su propia columna y, en otros momentos, como fijado para que múltiples usertypes puedan compartir una columna. Incluso si eso fuera válido, que no se basa en cómo se define un dinero, ¿cómo sabría Nhibernate lo que quería hacer cuando? El sistema no puede solo saber lo que quiere en un momento dado. Ahora también necesitaría ahorrar datos adicionales con el tipo de dinero que especificó cómo se pretendía utilizar un valor dado.

En pocas palabras, sus columnas como es no se pueden asignar como un UserType. Si no puede cambiar el esquema, la única forma en que puede hacerlo es mapear las tres columnas como primitivas normales y luego construir (y deconstruir) un tipo de dinero en su código de aplicación.

¿Y para lo que termina? Estoy realmente sorprendido de que incluso alguien como Fowler sugiera este enfoque como algún tipo de "mejor práctica" sin considerar realmente los detalles reales. El hecho es que la mayoría de los datos son un conjunto en el que la moneda está dictada para una fila por algunos factores a menudo implícitos o externos, como el país de origen o el país, donde opera una empresa, etc.

Las ocasiones en que en realidad incluso necesite moneda, a menudo tiene mucho otro equipaje que surge de múltiples monedas, como los tipos de cambio actuales, etc., que tener la moneda como parte y la parcela de un tipo de dinero ni siquiera es tan útil. Es una conveniencia, pero para los datos que son muy inconvenientes trabajar y no se puede hacer de otra manera. La mayor parte de la moneda de tiempo es fija y generalmente incluso se puede inferir. Por lo tanto, un tipo de dinero en casos típicos no puede acercarse a lo que realmente necesita: una conversión, o es simplemente información innecesaria. Con pocas excepciones, el tipo de dinero se convierte en solo un dingleberry en el culo de la solicitud.

Antes de pasar mucho tiempo tratando de implementar algo por posiblemente poca o ninguna otra razón que de alguna manera la gente haya comenzado a llamar a esto "mejor práctica", pregúntese si alguna vez necesitará usarlo para algo para algo.

Otros consejos

No es posible porque no tiene sentido cuando consideras leyendo y escribiendo entidades.

Considere el caso donde el valor1 es USD 20 y value2 es GBP 40. ¿Cómo lo persistirías?

¿Qué hay de cambiar tus clases de esta manera?

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

Ahora puede escribir un tipo de usuario para MoneyCollection.

Nota: debe asegurarse de que el MoneyCollection tiene un número constante, o al menos un número máximo de valores, porque debe asignarlo a un número constante de columnas. Verifique esto en la clase en sí.

Solo soy un novato solo aprendiendo y trabajando con NHibernate durante unos meses, pero ¿no es posible hacer un complejo UserType (IenHancedUsertype) que escribe en múltiples columnas, mientras que una columna será redundante?

Difícil de explicar, pero si asigna una propiedad en su entidad a la columna de divisas que se encarga de las acciones de lectura/escritura y se refiere a la misma columna varias veces dentro de la compleja mapeo de userType y apaga la inserción y actualización, lo que lo convierte en un Columna Readonly, ¿no funcionaría eso? He estado pensando en tratar de hacer un UserType como este para ver si funciona (ya que aún no lo he probado, no tengo ningún código de ejemplo ATM)

Sé que llega unos años tarde, pero tengo una solución.

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

Cartografía:

Map(Reveal.Member<Basket>("_subTotal")).Column("SubTotal");
Map(Reveal.Member<Basket>("_shipping")).Column("Shipping");
Map(Reveal.Member<Basket>("_currency")).Column("Currency");
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top