1
votes

Dapper avec mappage par code: multi-mappage avec des noms de colonnes répétitifs

J'essaye d'effectuer une requête simple et les données de résultat sont presque toutes nulles .

J'ai cette structure de table

Registres de table

XXX

et table Macro_tareas

string sql = @"SELECT reg.ID, mac.ID 
    FROM Registros as reg INNER JOIN Macro_tarea as mac on reg.TareaM_Id = mac.ID
    WHERE Fecha = @Fecha";

using (IDbConnection db = new OleDbConnection(ConnectionString))
{
    var result = db.Query<Registro,MacroTarea, Registro>(sql, 
    (reg,mac) =>
    {
        reg.MacroTarea = mac;
        return reg;
    }
    ,new { @Fecha = new DateTime(2019, 1, 4).Date }
    , splitOn: "mac.ID")
    .AsList();                                            
}

J'ai mappé les classes en C # comme ceci:

[Table("Registros")]
public class Registro
{
    [Column("ID")]
    public virtual int ID { get; set; }

    [Column("Fecha")]
    public virtual DateTime Fecha { get; set; }

    [Column("TareaM_Id")]
    public virtual int TareaM_Id { get; set; }

    public virtual MacroTarea MacroT { get; set; }
}

[Table("Macro_tarea")]
public class MacroTarea
{
    [Column("ID")]
    public virtual int ID { get; set; }

    [Column("Nombre")]
    public virtual string Nombre{ get; set; }

    public virtual ICollection<Registro> Registros { get; set; }

}

C'est la requête que j'essaie d'utiliser

ID            |  Autonumeric
Nombre        |  Short Text

J'essaie de récupérer uniquement les identifiants, mais les deux id deviennent nuls pourquoi cela se produit-il? p >

Le fait est que si j'ajoute Registros.Fecha et Macro_tarea.Nombre à la requête, la valeur est correctement obtenue. Mais l'identifiant continue à venir nul.

Apparemment, le problème ne se produit qu'avec les identifiants. Je soupçonne que ce problème est dû à des noms de colonnes en double.

Je travaille avec Microsoft Access uniquement dans la distribution qui compte.

Ma question n'est pas similaire à doublon possible car j'ai les classes définies comme elles devraient être mappées.


0 commentaires

3 Réponses :



0
votes

Le problème était en fait le nom des propriétés.

Je l'ai résolu en utilisant le mappage de colonnes personnalisé pour le faire, j'ai eu deux solutions possibles:

Sans extensions em >

Tout d'abord, nous définissons un dictionnaire avec le nom de la colonne comme clé, et le nom de la propriété comme valeur

FluentMapper.Initialize((config) => 
{
    config.AddMap(new Macro_tareaMap());
});

Ensuite, nous définissons un délégué pour obtenir l'objet PropertyInfo de la propriété auquel nous avons l'intention d'attribuer l'alias du dictionnaire précédent

internal class Macro_tareaMap : EntityMap<Macro_tarea>
{
       internal Macro_tareaMap()
       {
            //Mi propiedad ID esta asociada a la columna Macro_tarea.ID
            Map(x => x.ID).ToColumn("Macro_tarea.ID");
       }
}

Maintenant, nous définissons un objet qui implémente le Interface ITypeMap utilisant l'implémentation CustomPropertyTypeMap

SqlMapper.SetTypeMap(typeof(Macro_tarea), MacroTareaMapper);
SqlMapper.SetTypeMap(typeof(Registros), RegistrosMapper);

Ensuite, nous les enregistrons

ITypeMap MacroTareaMapper = new CustomPropertyTypeMap(typeof(Macro_tarea),
                (type, columnName) => mapper(type, columnName));

ITypeMap RegistrosMapper = new CustomPropertyTypeMap(typeof(Registros),
                (type, columnName) => mapper(type, columnName));

Solution plus simple avec Dapper.FluentMap em>

Il est implémenté comme suit:

Nous créons une classe qui hérite de EntityMap et en utilisant la méthode Map nous définir quelle colonne correspond à chaque propriété. Par exemple,

var mapper = new Func<Type, string, PropertyInfo>((type, columnName) =>
            {
                if (columnMaps.ContainsKey(columnName))
                    return type.GetProperty(columnMaps[columnName]);
                else
                    return type.GetProperty(columnName);
            });

Ensuite, enregistrez-le simplement

IDictionary<string, string> columnMaps = new Dictionary<string, string>()
            {
                { "Macro_tarea.ID", "ID" },
                { "Registros.ID", "ID" }
            };

J'espère que cela aidera d'autres personnes!

Source: https://medium.com/dapper-net/custom-columns- mapping-1cd45dfd51d6


0 commentaires

1
votes

Renommer les colonnes de votre base de données car votre code ne peut pas gérer les données n'est pas une bonne idée. Dans le monde de la séparation des préoccupations, pourquoi votre base de données devrait-elle s'en soucier? Il y a de bonnes raisons de nommer les colonnes ID "Id", et il se peut que vous n'ayez même pas la possibilité de les changer.

Il y a un autre problème avec le mappage Dapper qui ne permet pas de renommer les colonnes; types répétés. Si vous essayez de mapper vers plus d'une instance d'une classe, Dapper devient confus et renommer les colonnes ne fonctionnera pas car vous renommerez les deux instances.

Voici la solution que j'ai trouvée. C'est similaire à beaucoup d'exemples qui utilisent un dictionnaire, sauf:

  • il peut être imbriqué à autant de niveaux que vous le souhaitez
  • peut faire face à la limite d'objets de Dappers 7
  • peut gérer les doublons de la même classe
  • peut être réutilisé, par exemple, pour Get, GetCurrent et GetAll

Dans cet exemple, il y a une enchère qui comporte de nombreux lots. Chaque Lot peut avoir 1 ou plusieurs Articles. Les articles peuvent être des packs d'articles. Les articles proviennent d'un catalogue limité et nous aimons les données relationnelles, donc une table Things contient les détails sur chaque article, comme la couleur, la taille, etc. Ici, nous n'obtenons qu'un seul lot, mais obtenir une vente aux enchères est la même chose avec un autre niveau en haut pour les enchères.

Paramètre 1 - Le SQL pour tout obtenir en une seule fois

Paramètre 2 - Un tableau de types de chaque objet, nous reviendrons. Pour cette raison, il est préférable de commander votre SELECT pour regrouper les champs dans les classes

Paramètre 3 - Appelez la méthode que nous sommes sur le point d'écrire avec le résultat SQL

Paramètre 4 - Tableau de paramètres standard pour le SQL. L'injection SQL est mauvaise, d'accord?

public async Task<List<Lot>> GetAll(int auctionId)
{
    using (var connection = new SqlConnection(_appSettings.ConnectionString))
    {
        await connection.OpenAsync();
        var result = new List<Lot>();
        await connection.QueryAsync($@"
            SELECT [Lot].*, 
                [Item].[Id], 
                [Item].[LotId], 
                [Item].[Notes], 
                itemDetails.[Id],
                itemDetails.[ThingId],
                itemDetails.[Colour], 
                itemDetails.[Size], 
                [SubItem].[Id], 
                [SubItem].[ItemId], 
                [SubItem].[Notes], 
                subItemDetails.[Id],
                subItemDetails.[ThinId],
                subItemDetails.[Colour], 
                subItemDetails.[Size]
            FROM [Lot]
                INNER JOIN [Item] ON [Item].[LotId] = [Lot].[Id]
                    LEFT JOIN [Thing] AS itemDetails ON itemDetails.[Id] = [Item].[ThingId]
                LEFT JOIN [SubItem] ON [SubItem].[ItemId] = [Item].[Id]
                    LEFT JOIN [Thing] AS subItemDetails ON subItemDetails.[Id] = [SubItem].[ThingId]
            WHERE [AuctionId] = @{nameof(auctionId)}
            ORDER BY [Lot].[Id], [Item].[Id], [Expansion].[Id];",
            new Type[] {
                typeof(Lot),
                typeof(Item),
                typeof(Thing),
                typeof(Expansion),
                typeof(Thing)
            }, 
            MapResult(result),
            new
            {
                AuctionId = auctionId
            }
        );

        return result.ToList();
    }
}

private Func<object[], Lot> MapResult(List<Lot> result)
{
    return (obj) =>
    {
        Lot lot = (Lot)obj[0];
        Item item = (Item)obj[1];
        Thing itemDetails = (Thing)obj[2];
        SubItem subItem = (SubItem)obj[3];
        Thing subItemDetails = (Thing)obj[4];
        if (lot != null)
        {
            if (result.Any(a => a.Id == lot.Id))
            {
                lot = result.First(a => a.Id == lot.Id);
            }
            else
            {
                result.Add(lot);
            }
        }
        if (item != null)
        {
            if (lot.Items.Any(i => i.Id == item.Id))
            {
                item = lot.Items.First(i => i.Id == item.Id);
            }
            else
            {
                lot.Items.Add(item.FromThing(itemDetails));
            }
        }
        if (expansion != null)
        {
            if (item.SubItems.Any(e => e.Id == subItem.Id) == false)
            {
                item.SubItems.Add(subItem.FromThing(subItemDetails));
            }
        }
            return null;
    };
}

MapResult est la viande du code. Il retourne un Func avec deux types, le tableau Type que nous avons défini ci-dessus et le type de retour, et prend une liste de l'objet de niveau supérieur. Je mappe ensuite chaque élément du tableau d'objets à un autre de son type réel. Cela facilite la lecture du code et permet d'accéder sans problème aux propriétés et méthodes de l'objet.

Ensuite, il s'agit de descendre dans la hiérarchie, en vérifiant à chaque étape s'il en existe déjà une avec une correspondance id, et permutez l'itérateur à une référence à celui-ci si c'est le cas. Cela signifie que le code suivant s'ajoutera à l'élément existant.

Dans le cas particulier, j'ai également ajouté une fonction FromThing pour permettre une combinaison plus facile des propriétés d'objet.


0 commentaires