我有一个返回多个结果集的存储过程。我正在用 dapper 执行此操作。
结果集之一是 Person JOIN Checks,其中 Person 可以有许多 Check。
最终目标是拥有具有检查对象集合的不同人员对象。
QueryMultiple
给我一个Sqlmapper.GridReader
。我看到过载SqlMapper.GridReader.Read()
需要一个Func<TFirst, TSecond, TReturn>
.
有没有一个如何使用它的例子?
这是我如何让它工作的:
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 属性。
我仍然很想听到更好的方法来做到这一点。
这是我使用的解决方案的一个版本。我回避了 Ronnie 在他的回答中提出的问题,它使用继承层次结构而不是将属性设置为 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;
}
在 Ronnie 对设置他的答案person.Check = null
感到焦虑的地方,我担心将类添加到我的模型中会增加我的答案的复杂性。但我看不到一个简单的解决方法。PartialDataCollection
(注意,我在 Dapper GitHub 项目中提出了这个问题。)