pheromix
pheromix

Reputation: 19307

Linq to objects and lambda expression

There are classes Client and Commande :

public class Client {
    public int Identifiant { get; set; }
    public string Nom { get; set; }
    public int Age { get; set; }
}

public class Commande
{
    public int Identifiant {get; set;}
    public int IdentifiantClient {get;set;}
    public decimal Prix {get;set;}
}

Here are some lists based on these classes :

List<Client> listeClients = new List<Client>
{   new Client{Identifiant=1,Nom="Nicolas",Age=30},
    new Client{ Identifiant = 2, Nom = "Jérémie", Age = 20},
    new Client{ Identifiant=3, Nom="Delphine", Age=30},
    new Client{Identifiant = 4, Nom = "Bob", Age = 10}
};

List<Commande> listeCommandes = new List <Commande>
{
    new Commande{Identifiant=1, IdentifiantClient=1, Prix = 150.05M},
    new Commande{Identifiant=2, IdentifiantClient= 2, Prix= 30M},
    new Commande{Identifiant= 3, IdentifiantClient= 1, Prix= 99.99M},
    new Commande{Identifiant= 4, IdentifiantClient= 1, Prix= 100M},
    new Commande{Identifiant= 5, IdentifiantClient = 3, Prix = 80M},
    new Commande{Identifiant = 6, IdentifiantClient = 3,Prix = 10M}
};

Now there is this Linq expression :

var liste = from commande in listeCommandes
            join
            client in listeClients on commande.IdentifiantClient equals client.Identifiant
            group commande by new {commande.IdentifiantClient, client.Nom}
            into commandesGroupees
            let total = commandesGroupees.Sum(c => c.Prix)
            where total > 50
            orderby total 
            select new {
                commandesGroupees.Key.IdentifiantClient, 
                commandesGroupees.Key.Nom, 
                NombreDeCommandes = commandesGroupees.Count(),
                PrixTotal = total
            };

foreach(var element in liste)
{
    Console.WriteLine("Le client {0} ({1}) a réalisé {2} commande(s) pour un total de {3}", element.Nom, element.IdentifiantClient, element.NombreDeCommandes, element.PrixTotal);
}

In the Linq expression there is this expression : let total = commandesGroupees.Sum(c => c.Prix) . How is it possible that the parameter c represents an instanciation of the Command class ? because there is the calling of the property Prix !

Upvotes: 1

Views: 79

Answers (2)

Sweeper
Sweeper

Reputation: 271595

Let's analyse this query expression bit by bit.

from commande in listeCommandes

It is clear that if listeCommandes is a List<Commande>, commande should be of type Commande. Right?

join
client in listeClients on commande.IdentifiantClient equals client.Identifiant

This join joins the client list to the query. Now we have a client variable of type Client. But this doesn't have much to do with commande, which is still of type Commande.

group commande by new {commande.IdentifiantClient, client.Nom}
into commandesGroupees

This is where it gets interesting. When you group commande, a bunch of IGrouping<AnonymousClass, Commande> are created. This is exactly the commandesGroupees here.

So what happens if you call Sum on a IGrouping<AnonymousClass, Commande>? Remember that Sum is an extension method on IEnumerable<T>, and IGrouping<AnonymousClass, Commande> implements IEnumerable<Commandes>. This means that you are basically calling Sum on a IEnumerable<Commandes>, which expects a Function<Commande, decimal>.

Upvotes: 2

Ronan Thibaudau
Ronan Thibaudau

Reputation: 3603

Let's chunk it in smaller bits to make it simpler

from commande in listeCommandes 

commande is of type Commande

group commande by new { commande.IdentifiantClient, client.Nom } 

This is probably the bit confusing you, this still creates an IEnumerable<Commande> not an IEnumerable<anonymous<identifiantclient,nom>>, the new is what you want to group by, not what you want the grouped items to be, at this point you're creating an IGrouping that has IdentifiantClient and Nom as key but has an IEnumerable has an element

into commandesGroupees

Gives a name to the IGrouping we previously created

let total = commandesGroupees.Sum(c => c.Prix)

An IGrouping<TKey,TValue> is an IEnumerable<TValue> so as we've seen earlier each of the groups (grouped by idenfiantClient and Nom) hold a sequence of commands, since we're doing a linq query on it (sum) we're not concerned about the key but about the IEnumerable that is exposed (the IEnumerable<Command>).

This is why the sum works on Command and those have a Prix property.

Upvotes: 2

Related Questions