GQ08 VII: Linq to Sql mapping


Linq to Sql utilise des informations de mapping pour générer les requêtes Sql.
Comment récupérer ces informations de mapping pour notre propre usage ?

Imaginons un scénario simple pour générer dynamiquement les colonnes d’une grille (en mettant des combos pour les relations par exemple).

var db = new NorthwindDataContext(); var q = from c in db.Customers select c; grid.Columns.Add(...); grid.Columns.Add(...); ... grid.ItemsSource = q.ToList();
Comments (7)

  1. On pourrait envisager quelque chose comme ça :

    foreach (var p in from prop in typeof(Product).GetProperties()

                     where prop.GetCustomAttributes(typeof(ColumnAttribute), true).Any()

                     select prop)

       dataGridView1.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = p.Name, DataPropertyName = p.Name });

    foreach (var p in from prop in typeof(Product).GetProperties()

                     where prop.GetCustomAttributes(typeof(AssociationAttribute), true).Any()

                     select prop)

       dataGridView1.Columns.Add(new DataGridViewComboBoxColumn { HeaderText = p.Name, DataPropertyName = p.Name, DataSource = db.GetTable(p.PropertyType) });

    dataGridView1.DataSource = q;

  2. Simon says:

    var tableMetaType = db.Mapping.GetTable(typeof(Customer)).MetaType;

    foreach(var columnMetaData in tableMetaType.DataMembers)

    {

      if(columnMetaData.IsAssociation)

      {

          grid.Columns.Add(BuildEntityRefColumn(columnMetaData.Name, columnMetaData.Association.OtherType);

      }

      else

      {

         grid.Columns.Add(new ScalarColumn(columnMetaData.MappedName, columnMetaData.Name);

      }

    }

    Un truc dans le genre à priori (pas testé, ni même buildé ^^)

  3. Simon says:

    Mathieu, ton code ne fonctionne qu’avec le mapping par attribut :p OUNED !

  4. Miiitch says:

    Voici mon idée: je passe par le TypeDescriptor. On ajoute une méthode MapColumn<T>:

    var q =

       from c in db.Customers

       select c;

    MapColumn(grid,q);

    grid.ItemsSource = q.ToList();

    ———————-

    Et le code de la méthode (au moins la partie intéressante:

           private void MapColumn<T>(DataGridView gv, IEnumerable<T> result)

           {

               PropertyDescriptorCollection propertyCollection = TypeDescriptor.GetProperties(typeof(T));

               gv.Columns.Clear();

               foreach (PropertyDescriptor prop in propertyCollection)

               {

                   if (prop.PropertyType == typeof(bool))

                   {

                       // A remplir…

                   }

                   else if (prop.PropertyType == typeof(DateTime))

                   {

                       // A remplir…

                   }

                   else

                   {

                       // A remplir…

                   }

               }

           }

  5. Mitsu Furuta says:

    Et oui…petit piège, bien joué Simon.

    Rappel donc:

    – Linq to Sql utilise des metas informations sur son modèle objet pour mapper les informations physiques du modèle de base de données. Ex: quelle table correspond à quelle classe.

    – Linq to Sql propose deux types de définitions de ce mapping:

    1- par attribut

    2- via une définition xml

    La première solution, plus lisible lorsque l’on parcourt le code est intimement liée à la génération des entités et peut apparaître trop peu flexible.

    La solution xml permet d’associer dynamiquement un mapping à un datacontext instancié. Elle permet aussi facilement de créer des moteurs de personnalisation du mapping après utilisation de sqlmetal (outil en ligne de commande de génération du mapping depuis la base de données).

    Quoi qu’il en soit les deux méthodes définissent juste le mapping et le reste du moteur de requêtage de Linq to Sql est indépendant de ce choix.

    Si vous voulez développer une solution exploitant les métas informations de Linq to Sql qui prenne vraiment en compte ces deux définitions de mapping, le datacontext en offre une asbtraction via une petite API, voir la réponse de Simon ci-dessus.

  6. Mitsu Furuta says:

    Petite coquille, remplacez ‘MetaType’ par ‘RowType’ dans l’exemple de Simon