194 votes

Mappez manuellement les noms de colonnes avec les propriétés de classe

Je suis nouveau dans le micro ORM Dapper. Jusqu'à présent, je suis capable de l'utiliser pour des tâches simples liées à l'ORM mais je ne suis pas capable de mapper les noms des colonnes de la base de données avec les propriétés de la classe.

Par exemple, j'ai la table de base de données suivante :

Nom de la table : Personne
person_id  int
first_name varchar(50)
last_name  varchar(50)

et j'ai une classe appelée Personne :

public class Personne 
{
    public int PersonneId { get; set; }
    public string Prenom { get; set; }
    public string Nom { get; set; }
}

Veuillez noter que les noms de mes colonnes dans la table sont différents du nom de propriété de la classe à laquelle j'essaie de mapper les données que j'ai obtenues du résultat de la requête.

var sql = @"select top 1 PersonneId,Prenom,Nom from Personne";
using (var conn = ConnectionFactory.GetConnection())
{
    var personne = conn.Query(sql).ToList();
    return personne;
}

Le code ci-dessus ne fonctionnera pas car les noms des colonnes ne correspondent pas aux propriétés de l'objet (Personne). Dans ce scénario, y a-t-il quelque chose que je peux faire dans Dapper pour mapper manuellement (par exemple person_id => PersonneId) les noms des colonnes avec les propriétés de l'objet ?

0 votes

6voto

christo8989 Points 2303

Cela fait suite à d'autres réponses. C'est juste une idée que j'avais pour gérer les chaînes de requête.

Person.cs

public class Person 
{
    public int PersonId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public static string Select() 
    {
        return $"select top 1 person_id {nameof(PersonId)}, first_name {nameof(FirstName)}, last_name {nameof(LastName)}from Person";
    }
}

Méthode API

using (var conn = ConnectionFactory.GetConnection())
{
    var person = conn.Query(Person.Select()).ToList();
    return person;
}

3voto

La solution simple au problème que Kaleb essaie de résoudre est simplement d'accepter le nom de la propriété si l'attribut de colonne n'existe pas :

Dapper.SqlMapper.SetTypeMap(
    typeof(T),
    new Dapper.CustomPropertyTypeMap(
        typeof(T),
        (type, columnName) =>
            type.GetProperties().FirstOrDefault(prop =>
                prop.GetCustomAttributes(false)
                    .OfType()
                    .Any(attr => attr.Name == columnName) || prop.Name == columnName)));

2voto

CEPOCTb Points 11

La manière la plus facile (identique à la réponse de @Matt M mais corrigée et ajout de retour au map par défaut)

// substituer TypeMapProvider pour retourner une map personnalisée pour chaque type demandé
Dapper.SqlMapper.TypeMapProvider = type =>
   {
       // créer une map de type par défaut de secours
       var fallback = new DefaultTypeMap(type);
       return new CustomPropertyTypeMap(type, (t, column) =>
       {
           var property = t.GetProperties().FirstOrDefault(prop =>
               prop.GetCustomAttributes(typeof(ColumnAttribute))
                   .Cast()
                   .Any(attr => attr.Name == column));

           // si aucune propriété ne correspond - retour à la map de type par défaut
           if (property == null)
           {
               property = fallback.GetMember(column)?.Property;
           }

           return property;
       });
   };

1voto

Uri Abramson Points 884

Pour tous ceux qui utilisent Dapper 1.12, voici ce que vous devez faire pour y arriver :

  • Ajoutez un nouvel attribut de colonne de classe :

      [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property]
    
      public class ColumnAttribute : Attribute
      {
    
        public string Name { get; set; }
    
        public ColumnAttribute(string name)
        {
          this.Name = name;
        }
      }
  • Recherchez cette ligne :

    map = new DefaultTypeMap(type);

    et commentez-la.

  • Écrivez ceci à la place :

            map = new CustomPropertyTypeMap(type, (t, columnName) =>
            {
              PropertyInfo pi = t.GetProperties().FirstOrDefault(prop =>
                                prop.GetCustomAttributes(false)
                                    .OfType()
                                    .Any(attr => attr.Name == columnName));
    
              return pi != null ? pi : t.GetProperties().FirstOrDefault(prop => prop.Name == columnName);
            });

0 votes

Je ne suis pas sûr de comprendre - recommandez-vous aux utilisateurs de modifier Dapper pour rendre possible la mise en correspondance d'attributs par colonnes ? Si c'est le cas, c'est possible en utilisant le code que j'ai posté ci-dessus sans apporter de modifications à Dapper.

1 votes

Mais alors, vous devrez appeler la fonction de mapping pour chacun de vos types de modèle, n'est-ce pas ? Je suis intéressé par une solution générique afin que tous mes types puissent utiliser l'attribut sans avoir à appeler le mapping pour chaque type.

2 votes

Je voudrais voir DefaultTypeMap être implémenté en utilisant un motif de stratégie de sorte qu'il puisse être remplacé pour la raison mentionnée par @UriAbramson. Voir code.google.com/p/dapper-dot-net/issues/detail?id=140

1voto

GameSalutes Points 774

La solution de Kaleb Pederson a fonctionné pour moi. J'ai mis à jour le ColumnAttributeTypeMapper pour autoriser un attribut personnalisé (j'avais besoin de deux mappings différents sur le même objet de domaine) et j'ai mis à jour les propriétés pour autoriser des setters privés dans les cas où un champ devait être dérivé et les types différaient.

public class ColumnAttributeTypeMapper : FallbackTypeMapper where A : ColumnAttribute
{
    public ColumnAttributeTypeMapper()
        : base(new SqlMapper.ITypeMap[]
            {
                new CustomPropertyTypeMap(
                   typeof(T),
                   (type, columnName) =>
                       type.GetProperties( BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(prop =>
                           prop.GetCustomAttributes(true)
                               .OfType()
                               .Any(attr => attr.Name == columnName)
                           )
                   ),
                new DefaultTypeMap(typeof(T))
            })
    {
        //
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X