Tutti conosciamo i Dire Straits, vero? Bene.

La miglior formazione dei Dire Straits, a mio avviso, è stata quella composta da:

Se provassimo a mappare il tutto in un modello ad oggetti, potremmo scrivere:

public class Band
{
    public int BandId { get; set; }
    public IList<Player> Members { get; set; }
}

public class Player
{
    public int PlayerId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Band Band { get; set; }
}

E ora che abbiamo Linq2NHibernate possiamo scrivere una cosa del tipo:

using (ISession session = SessionHelper.OpenSession())
{
    Band myBand = session.Linq<Band>().Expand("Members")
                    .Where<Band>(bnd => bnd.BandId == 1)
                    .FirstOrDefault<Band>();

    Console.WriteLine("Nome band: {0}, numero di componenti: {1}",
         myBand.Name,
         myBand.Members.Count);
}

L’output però non è quello che ci aspettiamo:

Nome band: Dire Straits, numero di componenti: 1

La causa è nel codice SQL generato da Linq2NHibernate, che, da Profiler, risulta essere questo:

exec sp_executesql N'SELECT top 1 this_.BandId as BandId4_1_, this_.Name as Name4_1_,
    members2_.BandId as BandId3_, members2_.PlayerId as PlayerId3_,
    members2_.PlayerId as PlayerId2_0_, members2_.FirstName as FirstName2_0_,
    members2_.LastName as LastName2_0_, members2_.BandId as BandId2_0_
FROM dbo.Band this_
    left outer join dbo.Player members2_ on this_.BandId=members2_.BandId
WHERE this_.BandId = @p0',N'@p0 int',@p0=1

In pratica, la chiamata a FirstOrDefault() aggiunge quel TOP 1 all’SQL generato, che fa sì che venga ritornata solo una riga ritornata dal DB, falsando il conteggio.
I modi per risolvere la questione sono due:

  1. Tornare ad usare la Criteria API :).
  2. Sostituire FirstOrDefault<Band>() con ToList<Band>()[0].

In questo modo, la clausola TOP sparisce e i Dire Straits tornano ad essere cinque:

Nome band: Dire Straits, numero di componenti: 5