Dapper中的QueryMultiple多重映射

22

我有一个存储过程,返回多个结果集。我正在使用 dapper 执行。

其中一个结果集是 Person JOIN Checks,其中 Person 可以拥有多个 Checks。

最终目标是得到不同的 person 对象,并具有一个 check 对象的集合。

QueryMultiple 给了我一个 Sqlmapper.GridReader。我看到了一个重载的 SqlMapper.GridReader.Read(),它需要一个 Func<TFirst, TSecond, TReturn>

有没有使用它的示例?

2个回答

10

以下是我如何让其正常工作的方法:

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

正如评论所说,我不喜欢Person对象上的不必要的check属性。

我仍然很想听到更好的解决方法。


2
+1,但如果您正在寻找更好的方法,那么也许不接受这个答案可能更值得,因为未回答的问题似乎会吸引更多关注。 - dumbledad
“CheckId”是什么,你从哪里得到的? - dumbledad
明白了 - 这是 splitOn: 参数。 - dumbledad
p.CheckAlert和person.Check有什么区别? - dumbledad
所有这些东西都来自我的模型。 - Ronnie Overby
当Dapper能够轻松处理这种情况时,无需自己编写解决方案。https://github.com/StackExchange/Dapper/issues/452#issuecomment-182505793 - eddy

9
这是我使用的解决方案的一个版本。我通过继承层次结构而非将属性设置为null来回避Ronnie在他的答案中提出的问题,但本质上是相同的。
以下是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;
}

Ronnie担心在他的回答中设置person.Check = null,而我则担心将类PartialDataCollection添加到我的模型会增加额外的复杂性。但是我看不到简单的解决方法。

(注:我已经在Dapper GitHub项目上提出了这个问题。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接