Подтвердить что ты не робот

Dapper Multi Mapping с QueryMultiple

У меня есть хранимая процедура, которая возвращает несколько наборов результатов. Я выполняю это с dapper.

Один из наборов результатов - проверка личности человека, где у человека может быть много проверок.

Конечная цель состоит в том, чтобы иметь отдельные объекты человека, которые имеют коллекцию проверяющих объектов.

QueryMutliple дает мне Sqlmapper.GridReader. Я вижу перегрузку SqlMapper.GridReader.Read(), которая принимает Func<TFirst, TSecond, TReturn>.

Есть ли пример использования этого?

4b9b3361

Ответ 1

Вот как я получил работу:

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();

Как говорится в комментарии, мне не нравится ненужное свойство check на объекте Person.

Мне все равно хотелось бы услышать лучший способ сделать это.

Ответ 2

Вот вариант решения, которое я использовал. Я поднял вопрос, который Ронни поднял в своем ответе, используя иерархию наследования вместо того, чтобы присвоить свойству значение null, но это почти то же самое.

Здесь SQL: пользователи имеют элементы и коллекции, а элементы могут быть в коллекциях.

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))

Теперь классы моделей данных. Коллекции немного сложнее, чем я ожидал, чтобы иметь дело с многократным отображением Dapper с несколькими запросами.

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

Наконец, у нас есть метод контроллера, который использует множественное отображение Dapper с несколькими запросами

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

Где Ронни беспокоится о настройке person.Check = null в его ответе. Меня беспокоит дополнительная сложность моего ответа от добавления класса PartialDataCollection к моей модели. Но я не вижу в этом простого способа.

(N.B. Я поднял это как проблему в проекте Dapper GitHub.)