Share via


Comment : remplir des collections d'objets issues de plusieurs sources (LINQ)

Mise à jour : Juillet 2008

Cet exemple indique comment fusionner les données de types de sources différents en une séquence de nouveaux types. Les exemples du code suivant fusionnent des chaînes avec des tableaux d'entiers. Toutefois, le même principe s'applique à n'importe quelles sources de données, y compris les combinaisons d'objets en mémoire (incluant les résultats des requêtes LINQ to SQL, des groupes de données ADO.NET et des documents XML).

Remarque :

N'essayez pas de joindre des données en mémoire ou des données dans le système de fichiers avec les données qui sont encore dans une base de données. De telles jointures interdomaines peuvent générer des résultats indéfinis en raison des différentes méthodes de définition des opérations de jointure pour des requêtes de base de données et d'autres types de sources. En outre, une telle opération risque de provoquer une exception de mémoire insuffisante si la quantité de données dans la base de données est suffisamment grande. Pour joindre des données d'une base de données à des données en mémoire, appelez d'abord ToList ou ToArray sur la requête de base de données, puis effectuez la jointure sur la collection retournée.

Pour créer le fichier de données

Exemple

L'exemple suivant montre comment utiliser un type nommé Student pour stocker des données fusionnées à partir de deux collections en mémoire de chaînes qui simulent des données de feuille de calcul au format .csv. La première collection de chaînes représente les noms et les ID d'étudiants et la deuxième collection représente l'ID de l'étudiant (dans la première colonne) et quatre notes obtenues à un examen.

Class Student
    Public FirstName As String
    Public LastName As String
    Public ID As Integer
    Public ExamScores As List(Of Integer)
End Class

Class PopulateCollections

    Shared Sub Main()

        ' Join content from spreadsheets into a list of Student objectss.
        ' names.csv contains the student name
        ' plus an ID number. scores.csv contains the ID and a 
        ' set of four test scores. The following query joins
        ' the scores to the student names by using ID as a
        ' matching key, and then projects the results into a new type.

        Dim names As String() = System.IO.File.ReadAllLines("../../../names.csv")
        Dim scores As String() = System.IO.File.ReadAllLines("../../../scores.csv")

        ' Name:    Last[0],       First[1],  ID[2],     Grade Level[3]
        '          Omelchenko,    Svetlana,  111,       2
        ' Score:   StudentID[0],  Exam1[1]   Exam2[2],  Exam3[3],  Exam4[4]
        '          111,           97,        92,        81,        60

        ' This query joins two dissimilar spreadsheets based on common ID value.
        ' Multiple from clauses are used instead of a join clause
        ' in order to store results of id.Split.
        ' Note the dynamic creation of a list of ints for the
        ' TestScores member. We skip 1 because the first string
        ' in the array is the student ID, not an exam score.
        Dim scoreQuery1 = From name In names _
                         Let n = name.Split(New Char() {","}) _
                         From id In scores _
                         Let s = id.Split(New Char() {","}) _
                         Where n(2) = s(0) _
                         Select New Student() _
                         With {.FirstName = n(0), .LastName = n(1), .ID = n(2), _
                               .ExamScores = (From scoreAsText In s Skip 1 _
                                             Select Convert.ToInt32(scoreAsText)).ToList()}

        ' Optional. Store the query results for faster access
        ' in future queries. May be useful with very large data files.
        Dim students As List(Of Student) = scoreQuery1.ToList()

        ' Display the list contents
        ' and perform a further calculation
        For Each s In students
            Console.WriteLine("The average score of " & s.FirstName & " " & _
                              s.LastName & " is " & s.ExamScores.Average())
        Next

        ' Keep console window open in debug mode.
        Console.WriteLine("Press any key to exit.")
        Console.ReadKey()
    End Sub
End Class
' Output: 
'The average score of Adams Terry is 85.25
'The average score of Fakhouri Fadi is 92.25
'The average score of Feng Hanying is 88
'The average score of Garcia Cesar is 88.25
'The average score of Garcia Debra is 67
'The average score of Garcia Hugo is 85.75
'The average score of Mortensen Sven is 84.5
'The average score of O'Donnell Claire is 72.25
'The average score of Omelchenko Svetlana is 82.5
'The average score of Tucker Lance is 81.75
'The average score of Tucker Michael is 92
'The average score of Zabokritski Eugene is 83
class Student
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int ID { get; set; }
    public List<int> ExamScores { get; set; }
}

class PopulateCollections
{
    static void Main()
    {
        // These data files are defined in How to: Join Content from Dissimilar Files (LINQ) 
        string[] names = System.IO.File.ReadAllLines(@"../../../names.csv");
        string[] scores = System.IO.File.ReadAllLines(@"../../../scores.csv");

        // Merge the data sources using a named type.
        // var could be used instead of an explicit type.
        // Note the dynamic creation of a list of ints for the
        // TestScores member. We skip 1 because the first string
        // in the array is the student ID, not an exam score.
        IEnumerable<Student> queryNamesScores =
            from name in names
            let x = name.Split(',')
            from score in scores
            let s = score.Split(',')
            where x[2] == s[0]
            select new Student()
            {
                FirstName = x[0],
                LastName = x[1],
                ID = Convert.ToInt32(x[2]),
                ExamScores = (from scoreAsText in s.Skip(1)
                              select Convert.ToInt32(scoreAsText)).
                              ToList()
            };

        // Optional. Store the newly created student objects in memory
        // for faster access in future queries. Could be useful with
        // very large data files.
        List<Student> students = queryNamesScores.ToList();

        // Display the results and perform one further calculation.
        foreach (var student in students)
        {
            Console.WriteLine("The average score of {0} {1} is {2}.",
                student.FirstName, student.LastName, student.ExamScores.Average());
        }

        //Keep console window open in debug mode
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output: 
    The average score of Adams Terry is 85.25.
    The average score of Fakhouri Fadi is 92.25.
    The average score of Feng Hanying is 88.
    The average score of Garcia Cesar is 88.25.
    The average score of Garcia Debra is 67.
    The average score of Garcia Hugo is 85.75.
    The average score of Mortensen Sven is 84.5.
    The average score of O'Donnell Claire is 72.25.
    The average score of Omelchenko Svetlana is 82.5.
    The average score of Tucker Lance is 81.75.
    The average score of Tucker Michael is 92.
    The average score of Zabokritski Eugene is 83.
 */

Dans ces exemples, les sources de données sont initialisées avec des initialiseurs d'objets. La requête utilise une clause join pour faire correspondre les noms aux notes. L'ID est utilisé comme clé étrangère. Toutefois, dans une source, l'ID est une chaîne, et dans l'autre source, il s'agit d'un entier. Étant donné qu'une clause join requiert une comparaison d'égalité, vous devez d'abord extraire l'ID de la chaîne et le convertir en entier. Cette opération s'effectue dans les deux clauses let. L'identificateur temporaire x dans la première clause let stocke un tableau de trois chaînes créées après fractionnement de la chaîne d'origine à chaque espace. L'identificateur n dans la deuxième clause let stocke le résultat de la conversion de la sous-chaîne d'ID en entier. Dans la clause select, un initialiseur d'objet est utilisé pour instancier chaque nouvel objet Student à l'aide des données des deux sources.

Si vous n'avez pas besoin de stocker les résultats d'une requête, les types anonymes peuvent être plus pratiques que les types nommés. Les types nommés sont requis si vous passez les résultats de la requête à l'extérieur de la méthode dans laquelle la requête est exécutée. L'exemple suivant effectue la même tâche que l'exemple précédent, mais utilise des types anonymes au lieu de types nommés :

' This query uses an anonymous type
' Note the dynamic creation of a list of ints for the
' TestScores member. We skip 1 because the first string
' in the array is the student ID, not an exam score.
Dim scoreQuery2 = From name In names _
                 Let n = name.Split(New Char() {","}) _
                 From id In scores _
                 Let s = id.Split(New Char() {","}) _
                 Where n(2) = s(0) _
                 Select New With {.Last = n(0), _
                                  .First = n(1), _
                                  .TestScores = (From scoreAsText In s Skip 1 _
                                     Select Convert.ToInt32(scoreAsText)).ToList()}

' Display the list contents
' and perform a further calculation
For Each s In scoreQuery2
    Console.WriteLine("The average score of " & s.First & " " & s.Last & " is " & s.TestScores.Average())
Next
// Merge the data sources by using an anonymous type.
// Note the dynamic creation of a list of ints for the
// TestScores member. We skip 1 because the first string
// in the array is the student ID, not an exam score.
var queryNamesScores2 =
    from name in names
    let x = name.Split(',')
    from score in scores
    let s = score.Split(',')
    where x[2] == s[0]
    select new 
    {
        First = x[0],
        Last = x[1],
        TestScores = (from scoreAsText in s.Skip(1)
                      select Convert.ToInt32(scoreAsText))
                      .ToList()
    };

// Display the results and perform one further calculation.
foreach (var student in queryNamesScores2)
{
    Console.WriteLine("The average score of {0} {1} is {2}.",
        student.First, student.Last, student.TestScores.Average());
}

Compilation du code

  • Créez un projet Visual Studio qui cible la version 3.5 du .NET Framework. Par défaut, le projet possède une référence à System.Core.dll et une directive using (C#) ou une instruction Imports (Visual Basic) pour l'espace de noms System.Linq.

  • Copiez ce code dans votre projet.

  • Appuyez sur F5 pour compiler et exécuter le programme.

  • Appuyez sur n'importe quelle touche pour quitter la fenêtre de console.

Voir aussi

Concepts

LINQ et chaînes

Référence

Initialiseurs d'objets et de collection (Guide de programmation C#)

Types anonymes (Guide de programmation C#)

Historique des modifications

Date

Historique

Raison

Juillet 2008

Ajout d'un second jeu d'exemples de code

Résolution des bogues de contenu.