NHibernate Eager Obteniendo MĂșltiples Niveles
fetching-strategy (5)
Tengo una jerarquía de entidades de tres niveles: Customer-Order-Line, que me gustaría recuperar en su totalidad para un cliente dado, usando ISession.Get (id). Tengo los siguientes fragmentos XML:
customer.hbm.xml:
<bag name="Orders" cascade="all-delete-orphan" inverse="false" fetch="join">
<key column="CustomerID" />
<one-to-many class="Order" />
</bag>
order.hbm.xml:
<bag name="Lines" cascade="all-delete-orphan" inverse="false" fetch="join">
<key column="OrderID" />
<one-to-many class="Line" />
</bag>
He utilizado el atributo fetch = "join" para indicar que quiero buscar las entidades secundarias para cada padre, y esto ha construido el SQL correcto:
SELECT
customer0_.ID AS ID8_2_,
customer0_.Name AS Name8_2_,
orders1_.CustomerID AS CustomerID__4_,
orders1_.ID AS ID4_,
orders1_.ID AS ID9_0_,
orders1_.PostalAddress AS PostalAd2_9_0_,
orders1_.OrderDate AS OrderDate9_0_,
lines2_.OrderID AS OrderID__5_,
lines2_.ID AS ID5_,
lines2_.ID AS ID10_1_,
lines2_.[LineNo] AS column2_10_1_,
lines2_.Quantity AS Quantity10_1_,
lines2_.ProductID AS ProductID10_1_
FROM Customer customer0_
LEFT JOIN [Order] orders1_
ON customer0_.ID=orders1_.CustomerID
LEFT JOIN Line lines2_
ON orders1_.ID=lines2_.OrderID
WHERE customer0_.ID=1
Hasta ahora, esto se ve bien: SQL devuelve el conjunto correcto de registros (con solo un orderid distinto), pero cuando ejecuto una prueba para confirmar el número correcto de entidades (desde NH) para pedidos y líneas, obtengo los resultados incorrectos
Debería obtener (de mis datos de prueba), 1xOrder y 4xLine, sin embargo, obtengo 4xOrder y 4xLine. Parece que NH no reconoce el grupo de "repetición" de información de orden en el conjunto de resultados, ni "reutiliza" correctamente la entidad de orden.
Estoy usando todos los ID enteros (PK), y he intentado implementar IComparable de T y IEquatable de T usando esta ID, con la esperanza de que NH vea la igualdad de estas entidades. También he intentado descartar Equals y GetHashCode para usar la ID. Ninguno de estos ''intentos'' ha tenido éxito.
¿Es la operación de recuperación de niveles múltiples una operación compatible para NH, y si es así, hay una configuración XML requerida (o algún otro mecanismo) para soportarla?
NB: utilicé la solución de sirocco con algunos cambios en mi propio código para finalmente resolver este. el xml debe cambiarse de una bolsa a otra, para todas las colecciones, y las entidades se cambiaron para implementar IComparable <>, que es un requisito de establecer un conjunto único.
public class BaseEntity : IComparable<BaseEntity>
{
...
private Guid _internalID { get; set; }
public virtual Guid ID { get; set; }
public BaseEntity()
{
_internalID = Guid.NewGuid();
}
#region IComparable<BaseEntity> Members
public int CompareTo( BaseEntity other )
{
if ( ID == Guid.Empty || other.ID == Guid.Empty )
return _internalID.CompareTo( other._internalID );
return ID.CompareTo( other.ID );
}
#endregion
...
}
Tenga en cuenta el uso de un campo InternalID. Esto es necesario para entidades nuevas (transitorias); de otro modo, no tendrán inicialmente una ID (mi modelo las ha suministrado cuando se guardaron).
Obtiene 4XOrder y 4XLines porque la unión con líneas duplica los resultados. Puede configurar un transformador en la ICriteria como:
.SetResultTransformer(new DistinctRootEntityResultTransformer())
@Tigraine: su consulta solo devuelve Publicaciones con comentarios. Esto trae todas las publicaciones con todos los comentarios (2 niveles). Lo que pide Ben es que el cliente ordene a LineItem (nivel 3). @Ben: que yo sepa, nHibernate aún no soporta la carga ansiosa de hasta 3 niveles. Hibernate lo apoya tú.
Acabo de leer el blogpost de Ayende donde utilizó el siguiente ejemplo:
session.CreateCriteria(typeof(Post))
.SetFetchMode("Comments", FetchMode.Eager)
.List();
En una consulta de criterios para evitar la carga diferida en una consulta en particular
Quizás eso pueda ayudarte.
Estaba teniendo el mismo problema. Ver este hilo No obtuve una solución, pero una pista de Fabio. Usa Set en lugar de bolsa. Y funcionó.
Entonces mi sugerencia es intentar usar el conjunto. No tiene que usar la colección Iesi use IDictonary y NH está feliz
public override IEnumerable<Baseline> GetAll()
{
var baselines = Session.CreateQuery(@" from Baseline b
left join fetch b.BaselineMilestones bm
left join fetch bm.BaselineMilestonePrevious ")
.SetResultTransformer(Transformers.DistinctRootEntity)
.List<Baseline>();
return baselines;
}
Si necesita mantener su uno-a-manys como bolsas, entonces puede emitir 2 consultas, cada una con solo 1 nivel de jerarquía. por ejemplo, algo como esto:
var temp = session.CreateCriteria( typeof( Order ) )
.SetFetchMode( "Lines", NHibernate.FetchMode.Eager )
.Add( Expression.Eq( "Customer.ID", id ) )
.List();
var customer = session.CreateCriteria( typeof( Customer ) )
.SetFetchMode( "Orders", NHibernate.FetchMode.Eager )
.Add( Expression.Eq( "ID", id ) )
.UniqueResult();
Las líneas se cargan en el caché NH en la primera consulta, por lo que no necesitarán una carga diferida cuando accedan más tarde, por ejemplo, al cliente. Pedidos [0]. Líneas [0].