NHibernate fluido: ¿Mapeo de componentes IEnumerable desde una sola consulta?

Espero que puedas ayudarme con un problema de mapeo que tengo. Por el momento tengo una vista que devuelve algo como:

Nombre | Título | Precio | Fecha | Formato | FormatPriority |

Los datos de ejemplo pueden ser:

Bob | Créditos | 340 | 01/01/2010 | BAR | 10 |
Bob | Créditos | 340 | 01/01/2010 | FOO | 20 |
Bob | Créditos | 340 | 01/01/2010 | WOO | 40 |

Lo que quiero es un modelo de dominio que se vea así:

Nombre de cadena;
Título de la cadena;
int Precio;
DateTime Date;
Formatos IEnumerable;

La clase de formato tendría entonces:

tipo de cadena
Prioridad int

Por el momento, estamos usando el enfoque ClassMap dentro de Fluent NHibernate (no configuración automática). ¿Cómo mapearíamos esto? El componente no parece admitir una colección y esta no es una relación HasMany, ya que regresa como parte de la misma consulta.

¿¿Algunas ideas??

Muchas Gracias

Ben

preguntado el 01 de julio de 10 a las 21:07

1 Respuestas

Descargo de responsabilidad: este es un truco tan grande que me duele publicarlo.

Esto se basa en el esquema que ha proporcionado, por lo que es posible que deba modificarlo para adaptarse a un diseño diferente. Probablemente haya una manera mucho mejor de hacer esto, pero con suerte, esto debería ayudarlo a continuar al menos.

El problema es que tiene un poco de discrepancia en su modelo y consulta. Su consulta devuelve múltiples filas que pretende ser para una sola entidad con múltiples componentes, pero NHibernate está diseñado para interpretar eso como múltiples entidades, cada una con un solo componente.

NHibernate admite colecciones de componentes, pero solo cuando están almacenados en una tabla / vista separada. Estos componentes se unen mediante una clave externa a la tabla de entidades. Si puede cambiar su diseño para que sea compatible con esto, ¡hágalo!

Si no es así, la única opción que se me ocurre es una autounión en su opinión. No producirá la consulta más optimizada, pero debería funcionar.

No mencionaste cómo se llamaba tu entidad, así que me fui con Transaction.

public class Transaction
{
    public virtual string Name { get; set; }
    public virtual string Title { get; set; }
    public virtual decimal Price { get; set; }
    public virtual DateTime Date { get; set; }
    public virtual ISet<Format> Formats { get; set; }
}

public class Format
{
    public virtual string Type { get; set; }
    public virtual int Priority { get; set; }

    // OVERRIDE EQUALITY MEMBERS!
}

El mapeo que he usado es:

public class TransactionMap : ClassMap<Transaction>
{
    public TransactionMap()
    {
        Table("vwTransactions");
        Id(x => x.Name);
        Map(x => x.Title);
        Map(x => x.Price);
        Map(x => x.Date);
        HasMany(x => x.Formats)
            .Table("vwTransactions")
            .KeyColumn("Name")
            .Component(c =>
            {
                c.Map(x => x.Type, "Format");
                c.Map(x => x.Priority, "FormatPriority");
            })
            .Fetch.Join();
    }
}

Para que pueda ver que el mapeo apunta a la vwTransactions vista. No especificaste una identificación en tu esquema, así que usé Name como identidad (esto es importante). Saltar al HasMany ahora, puede ver que también apunta a vwTransactions; NHibernate verá esto y se unirá automáticamente a la vista. Entonces la columna de la clave se establece en Name, el mismo que el Id de la entidad; de esta manera NHibernate lo usará para resolver las referencias entre el componente y la entidad, en lugar de intentar usar una clave externa entera. La Fetch.Join obligará a NH a buscar con entusiasmo esta relación, así que al menos ahorramos un poco allí. Lo último a destacar, el Formats la propiedad es una ISet, si no lo hace, terminará con componentes duplicados.

Si ahora crea una consulta de criterios (o hql) para Transaction, recuperará sus entidades con sus componentes; sin embargo, obtendrá duplicados debido a que se recuperan varias filas por entidad. Esto es bastante común y se resuelve fácilmente con el transformador DistinctRootEntity.

var transactions = session.CreateCriteria(typeof(Transaction))
  .SetResultTransformer(Transformers.DistinctRootEntity)
  .List<Transaction>();

Eso debería ser así, ahora terminará con una sola entidad (según su conjunto de datos) con 3 componentes.

Asqueroso, lo sé.

Respondido 02 Jul 10, 12:07

Si bien estoy seguro de que esto habría funcionado, seguí su consejo y cambié la forma en que las vistas devuelven datos. Ahora tengo una vista por relación ... espero que funcione :) - Ben Hall

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.