Given this domain class:
public class Product { public virtual int ProductId { get; set; } public virtual string ProductName { get; set; } public virtual string ProductDescription { get; set; } public virtual int YearIntroduced { get; set; } public virtual IList<Order> Orders { get; set; } }
The way to report product even with no orders:
Order orderAlias = null; var results = s.QueryOver<Product>() .Left.JoinAlias(od => od.Orders, () => orderAlias) .SelectList(selection => selection.SelectGroup(b => b.ProductId) .SelectGroup(b => b.ProductName) .SelectCount(b => orderAlias.OrderId)) .List<object[]>() .Select(zz => new { ProductId = (int)zz[0], ProductName = (string)zz[1], OrderCount = (int)zz[2] }); foreach (var item in results) { Console.WriteLine("{0} {1}", item.ProductName, item.OrderCount); }
That doesn't materialize the product orders to List<Order>. That's efficient, it uses COUNT, LEFT JOIN, GROUP BY.
However, the ugly consequence of using IQueryOver (aside from IQueryOver approach is also ugly) is we won't be able to use the more mockable and unit-test-friendly IQueryable. We can use IQueryable even on in-memory objects which makes IQueryable amenable for unit-testing. IQueryOver API looks very convoluted as compared to Linq's API simplicity; to contrast, here's how it looks like if NHibernate's Linq supported DefaultIfEmpty:
var www = from p in s.Query<Product> from o in p.Orders.DefaultIfEmpty() group o by new { p.ProductId, p.ProductName } into grp select new { grp.Key.ProductId, grp.Key.ProductName, OrderCount = grp.Sum(x => x.Product != null ? 1 : 0 ) };
No comments:
Post a Comment