Dapper Multi Mapping avec QueryMultiple

.net dapper sql

Question

J'ai une procédure stockée qui renvoie plusieurs jeux de résultats. Je l'exécute avec dapper.

L'un des ensembles de résultats est le contrôle de la personne, où la personne peut avoir plusieurs contrôles.

L'objectif final est d'avoir des objets de personne distincts possédant une collection d'objets de contrôle.

QueryMutliple me donne un Sqlmapper.GridReader . Je vois une surcharge de SqlMapper.GridReader.Read() qui prend un Func<TFirst, TSecond, TReturn> .

Existe-t-il un exemple d'utilisation?

Réponse acceptée

Voici comment je l'ai fait fonctionner:

var q = _sqlConnection.QueryMultiple("MySproc",
                                     myParams,
                                     commandType: CommandType.StoredProcedure);
var set1 = q.Read<Set1Type>();

var set2Func = new Func<Person, Check, Person>((p, c) => {
    p.CheckAlert = c;
    return p;
});

var set2 = q.Read(set2Func, "CheckId")
            .GroupBy(x => x.PersonId)
            .Select(x => {
                var person = x.First();
                person.Checks = x.Select(p => p.Check).ToArray();
                person.Check = null; // i really don't like this
                return person;
            })
            .ToArray();

Comme le dit le commentaire, je n'aime pas la propriété de vérification inutile sur l'objet Person.

J'aimerais toujours entendre parler d'une meilleure façon de le faire.


Réponse populaire

Voici une version de la solution que j'ai utilisée. J'ai écarté le problème soulevé par Ronnie dans sa réponse en utilisant une hiérarchie d'héritage au lieu de définir une propriété sur null, mais cela revient à peu près à la même chose.

Voici le SQL: les utilisateurs ont des éléments et des collections, et les éléments peuvent être dans des collections.

CREATE TABLE Users
(id UNIQUEIDENTIFIER DEFAULT (NEWID()) NOT NULL,
name NVARCHAR (MAX) NULL,
email NVARCHAR (128) NULL,
PRIMARY KEY (id))

CREATE TABLE Items
(id UNIQUEIDENTIFIER DEFAULT (NEWID()) NOT NULL,
userId UNIQUEIDENTIFIER NOT NULL,
name NVARCHAR (MAX) NULL,
description NVARCHAR (MAX) NULL,
PRIMARY KEY (id),
FOREIGN KEY (userId) REFERENCES Users (id))

CREATE TABLE Collections
(id UNIQUEIDENTIFIER DEFAULT (NEWID()) NOT NULL,
userId UNIQUEIDENTIFIER NOT NULL,
name NVARCHAR (MAX) NULL,
layoutSettings NVARCHAR (MAX) NULL,
PRIMARY KEY (id),
FOREIGN KEY (userId) REFERENCES Users (id))

CREATE TABLE CollectedItems
(itemId UNIQUEIDENTIFIER NOT NULL,
collectionId  UNIQUEIDENTIFIER NOT NULL,
PRIMARY KEY CLUSTERED (itemId, collectionId),
FOREIGN KEY (itemId) REFERENCES Items (id),
FOREIGN KEY (collectionId) REFERENCES Collections (id))

Maintenant, les classes du modèle de données. Les collections sont un peu plus compliquées que ce à quoi je m'attendais pour gérer le multi-mapping Dapper avec plusieurs requêtes.

public class User
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public List<Item> Items { get; set; }
    public List<Collection> Collections { get; set; }
}

public class Item
{
    public Guid Id { get; set; }
    public Guid UserId { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

public class CoreCollection
{
    public Guid Id { get; set; }
    public Guid UserId { get; set; }
    public string Name { get; set; }
    public string LayoutSettings { get; set; }
}

public class PartialDataCollection : CoreCollection
{
    public Guid ItemId { get; set; }
}

public class Collection : CoreCollection
{
    public List<Guid> ItemIds { get; set; }
}

public class CollectedItem
{
    public Guid ItemId { get; set; }
    public Guid CollectionId { get; set; }
    public DateTime CreatedAt { get; set; }
}

Enfin, nous avons la méthode du contrôleur qui utilise le multi-mapping Dapper avec plusieurs requêtes

[Route("GetUser/{id}")]
public User GetUser(Guid id)
{
    var sql = @"SELECT * FROM Users WHERE id = @id
                SELECT * FROM Items WHERE userId = @id
                SELECT * FROM Collections 
                    LEFT OUTER JOIN CollectedItems ON Collections.id = CollectedItems.collectionId  
                    WHERE userId = @id";
    using (var connection = new SqlConnection(ConnectionString))
    {
        var multi = connection.QueryMultiple(sql, new { id = id });
        var user = multi.Read<User>().Single();
        var items = multi.Read<Item>().ToList();
        var partialDataCollections = multi.Read<PartialDataCollection, CollectedItem, PartialDataCollection>(AddCollectedItem, splitOn: "itemId").ToList();

        user.Items = items;

        user.Collections = partialDataCollections.GroupBy(
            pdc => pdc.Id,
            (key, group) => new Collection
            {
                Id = key,
                UserId = group.First().UserId,
                Name = group.First().Name,
                LayoutSettings = group.First().LayoutSettings,
                ItemIds = group.Select(groupMember => groupMember.ItemId).ToList()
            }).ToList();

        return user;
    }
}

private PartialDataCollection AddCollectedItem(PartialDataCollection collection, CollectedItem collectedItem)
{
    if (collection != null && collectedItem != null)
    {
        collection.ItemId = collectedItem.ItemId;
    }
    return collection;
}

Là où Ronnie est soucieux de définir person.Check = null dans sa réponse, je suis inquiet de la complexité supplémentaire de ma réponse à l’ajout de la classe PartialDataCollection à mon modèle. Mais je ne peux pas voir un moyen simple de contourner cela.

(NB j'ai soulevé cela comme un problème sur le projet Dapper GitHub.)



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi