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.
3 Réponses :
Comme nous l'avons expliqué dans les commentaires, il s'agit d'un problème dû aux noms de colonnes en double dans deux tables. C'est là que le problème et la solution similaires peuvent être trouvés. Mais, cela n'inclut pas le "mappage par code" comme vous l'avez dit. Ce n'est donc pas une copie exacte.
Je vous suggère de changer les noms des champs ID
dans vos tables pour éviter de les heurter. Bien entendu, vous devez également modifier le nom de vos propriétés et mappages POCO en conséquence.
Si vous ne pouvez pas modifier les noms de colonne dans la table, changez le nom de propriété POCO et utilisez l'alias de colonne dans la requête SQL pour correspondre à ces nouveaux noms de propriété.
J'espère que cela vous aidera.
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
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:
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.