Dapper Multi Mapping avec QueryMultiple

j'ai une procédure stockée qui renvoie plusieurs ensembles de résultats. Je suis de l'exécution de cette avec dapper.

L'un des ensembles de résultats Est des vérifications de jointure de la personne, où la personne peut avoir beaucoup de vérifications.

le but final est d'avoir des objets de personne distincts qui ont une collection d'objets de contrôle.

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

Est-il un exemple de comment l'utiliser?

19
demandé sur Ronnie Overby 2012-10-19 20:24:46

2 réponses

Voici comment je l'ai eu à travailler:

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 commentaire le dit, Je n'aime pas la propriété check non nécessaire sur L'objet Person.

j'aimerais encore entendre parler d'une meilleure façon de faire cela.

6
répondu Ronnie Overby 2016-02-05 10:01:42

Voici une version de la solution que j'ai utilisée. J'côté intensifié la question Ronnie soulevées dans le sa réponse utiliser une hiérachie d'héritage au lieu de définir une propriété à null, mais cela revient à peu près à la même chose.

voici le SQL: les utilisateurs ont des articles et des collections, et les articles 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 de modèles de données. Les Collections sont un peu plus compliquées que je m'y attendais pour traiter avec Dapper multi mapping 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 controller qui utilise Dapper multi mapping avec des requêtes multiples

[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;
}

où Ronnie est anxieux à propos de la mise en place person.Check = nullsa réponse je suis inquiet au sujet de la complexité supplémentaire dans ma réponse de l'ajout de la classe PartialDataCollection pour mon modèle. Mais je ne vois pas un simple moyen de contourner cela.

(N. B. j'ai soulevé ce que problème sur le projet Dapper GitHub.)

5
répondu dumbledad 2017-05-23 10:31:06