Supponiamo di avere le classi Customers e Orders del database northwind, abbiamo implementato la relazione many-to-one bidirezionale nel nostro domain model, così da avere una collection orders nella classe Customer, ed una proprietà Customer nell'oggetto order. Ora se si vuole prendere tutti gli ordini di "ALFKI" in un determinato range di tempo si può scrivere questa criteria query
ICriteria criteria = session.CreateCriteria(typeof (Order));
criteria.Add(Expression.Between("OrderDate", new DateTime(1997, 10, 1), new DateTime(1998, 1, 1)));
Customer cust = session.Load<Customer>("ALFKI");
criteria.Add(Expression.Eq("Customer", cust));
IList<Order> result = criteria.List<Order>();
Notare come il riferimento all'oggetto Customer sia recuperato tramite il Session.Load() cosi da creare un proxy e non andare a disturbare il database. Ma cosa succede se la relazione non è implementata in maniera bidirezionale, ma unidirezionale? Ad esempio se l'oggetto Order non ha nessuna proprietà che punta all'oggetto Customer? In questo caso è possibile fare una criteria query un po' "sporchina" dato che andiamo a referenziare direttamente i campi del nostro database. Ecco come fare:
ICriteria criteria = session.CreateCriteria(typeof(Order2));
criteria.Add(Expression.Between("OrderDate", new DateTime(1997, 10, 1), new DateTime(1998, 1, 1)));
criteria.Add(Expression.Sql("{alias}.CustomerId = ?", "ALFKI", NHibernateUtil.String));
IList<Order2> result = criteria.List<Order2>();
Come si può vedere NHibernate fornisce un comodissimo criterio Sql che permette di inserire condizioni su campi non mappati nel domain model, chiaramente questo è poco object oriented, ma è un altro degli strumenti da tenere nella cassetta degli attrezzi e da usare quando serve.
A questo punto però i puristi storcerebbero il naso, cosa succede se cambio il nome dei campi del db? Con Nhibernate non dovrei mai fare query che contengono il nome dei campi sql!!! Si lo so avete ragione ed infatti anche se abbiamo una relazione unidirezionale da Customer a Orders tramite la proprietà Orders di Customer possiamo fare uso di subquery tramite la Criteria API
DetachedCriteria subquery = DetachedCriteria.For(typeof (Customer2))
.SetProjection(Projections.Property("Id"))
.CreateAlias("Orders", "Orders")
.Add(Expression.Eq("Id", "ALFKI"))
.Add(Property.ForName("Orders.Id").EqProperty("order.Id"));
ICriteria criteria = session.CreateCriteria(typeof(Order2), "order");
criteria.Add(Expression.Between("OrderDate", new DateTime(1997, 10, 1), new DateTime(1998, 1, 1)));
criteria.Add(Subqueries.Exists(subquery));
IList<Order2> result = criteria.List<Order2>();
Assert.That(result.Count, Is.EqualTo(2));
Che genera la seguente query in SQL
exec sp_executesql N'SELECT this_.OrderId as OrderId1_0_, this_.OrderDate as OrderDate1_0_ FROM dbo.ORders this_ WHERE this_.OrderDate between @p0 and @p1 and exists (SELECT this_0_.CustomerId as y0_ FROM dbo.Customers this_0_ inner join dbo.ORders orders1_ on this_0_.CustomerId=orders1_.CustomerId WHERE this_0_.CustomerId = @p2 and orders1_.OrderId = this_.OrderId)', N'@p0 datetime,@p1 datetime,@p2 nvarchar(5)', @p0 = 'Oct 1 1997 12:00:00:000AM', @p1 = 'Jan 1 1998 12:00:00:000AM', @p2 = N'ALFKI'
Alk.
Technorati tags:NHibernate