如何将 Dapper 与条件 StringBuilder 查询一起使用?
How to use Dapper with conditional StringBuilder query?
我正在尝试构建一个动态查询字符串并将其应用于 dapper(以某种方式提供了与 Entity Framework 可选“.include(x=>x.XXX)
”功能类似的 "feel" ):
构建查询字符串
var query = new StringBuilder();
query.Append(" select * from test_post p ");
if (validIncludesToPerform[0])
query.Append(" left join sys_user u on u.Id = p.CreatorId ");
if (validIncludesToPerform[1])
query.Append(" left join test_poststatus s on s.Id = p.StatusId ");
query.Append(" where p.Id = @Id; ");
var isQueryMultiple = false;
if (validIncludesToPerform[2])
{
isQueryMultiple = true;
query.Append(" select * from test_postnote n where n.PostId = @Id; ");
}
if (validIncludesToPerform[3])
{
isQueryMultiple = true;
query.Append(@" select *
from test_post_tag pt
left join test_tag t on t.Id = pt.TagId
where pt.PostId = @Id; ");
}
处理查询
Post pst;
if (isQueryMultiple)
{
using (var multi = Connection.QueryMultiple(query.ToString(), new { Id = id }))
{
pst = multi.Read<Post, User, PostStatus, Post>((post, user, status) =>
{
if (post == null) return null;
if (user != null) post.Creator = user;
if (status != null) post.Status = status;
return post;
}).FirstOrDefault();
if (pst != null && validIncludesToPerform[2])
pst.Notes = multi.Read<PostNote>().ToList();
if (pst != null && validIncludesToPerform[3])
pst.PostTags = multi.Read<PostTag, Tag, PostTag>((pTag, tag) =>
{
if (pTag == null) return null;
if (tag != null) pTag.Tag = tag;
return pTag;
}).ToList();
}
}
else
{
pst = Connection.Query<Post, User, PostStatus, Post>
(query.ToString(), (post, user, status) =>
{
if (post == null) return null;
if (user != null) post.Creator = user;
if (status != null) post.Status = status;
return post;
}, new { Id = id }).FirstOrDefault();
}
重要说明: Dapper 假定您的 Id 列被命名为 "Id" 或 "id",如果您的主键不同或您想拆分"Id" 以外的点的宽行,使用可选的 'splitOn' 参数。这意味着在我的例子中我不必指定 "Id,Id" 因为这部分已经是隐式的。
- 处理
PostNotes
或 PostTags
的部分工作正常。
- Post "including"
User
和 Status
也工作正常。
- 如果 失败 当我尝试加载
User
"empty" 或仅使用 User
或 Status
...
错误信息: "When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id\r\nParameter name: splitOn"
原因:泛型的使用意味着查询将接收“3 个部分”,但它只是接收到一个较小的数字....
丑陋的解决方案:
我知道如果我只是将 Status
和 User
分成每个独立的查询,这会起作用:
if (validIncludesToPerform[0])
query.Append(@" select u.* from sys_user u right join test_post p on u.Id = p.CreatorId
where p.Id = @Id;");
if (validIncludesToPerform[1])
query.Append(@" select ps.* from test_poststatus ps right join test_post p on ps.Id = p.StatusId
where p.Id = @Id;");
但由于此信息是一对一的关系,我认为单个查询更好。所以,我尽量避免使用这个解决方案。
我也不想列出泛型签名的所有可能组合,例如:
Connection.Query<Post, User, PostStatus, Post>
Connection.Query<Post, User, Post>
Connection.Query<Post, PostStatus, Post>
Connection.Query<Post>
一旦我开始向这个 class 添加更多 1to1 关系,这种方法就不友好了。
我该如何解决这个问题?
您需要告诉 dapper 在何处拆分 - 对象之间的边界在您的 select statement.That 中是您的错误声明所说的位置。因此,将您的电话修改为:
pst = multi.Read<Post, User, PostStatus, Post>((post, user, status) =>
{
if (post == null) return null;
if (user != null) post.Creator = user;
if (status != null) post.Status = status;
return post;
}, splitOn: "Id,Id").FirstOrDefault();
split on 告诉 Dapper 在何处停止映射到一个对象并开始映射到另一个对象。由于您有 2 个联接,因此您需要拆分两个字段。确保将这两个放在一个由逗号分隔的字符串中,并且字符串中没有 space 个字符...
如需更好的解释,请参阅此 post:
Correct use of Multimapping in Dapper
我找到了一个 "somehow" 不太糟糕的方法:
var query = new StringBuilder();
query.Append(" select * from test_post p ");
query.Append(validIncludesToPerform[0]
? " left join sys_user u on u.Id = p.CreatorId "
: " left join sys_user u on 1 = 0 ");
query.Append(validIncludesToPerform[1]
? " left join test_poststatus s on s.Id = p.StatusId "
: " left join test_poststatus s on 1 = 0 ");
query.Append(" where p.Id = @Id; ");
我暂时留下这个答案,等待更好的选择....
我正在尝试构建一个动态查询字符串并将其应用于 dapper(以某种方式提供了与 Entity Framework 可选“.include(x=>x.XXX)
”功能类似的 "feel" ):
构建查询字符串
var query = new StringBuilder();
query.Append(" select * from test_post p ");
if (validIncludesToPerform[0])
query.Append(" left join sys_user u on u.Id = p.CreatorId ");
if (validIncludesToPerform[1])
query.Append(" left join test_poststatus s on s.Id = p.StatusId ");
query.Append(" where p.Id = @Id; ");
var isQueryMultiple = false;
if (validIncludesToPerform[2])
{
isQueryMultiple = true;
query.Append(" select * from test_postnote n where n.PostId = @Id; ");
}
if (validIncludesToPerform[3])
{
isQueryMultiple = true;
query.Append(@" select *
from test_post_tag pt
left join test_tag t on t.Id = pt.TagId
where pt.PostId = @Id; ");
}
处理查询
Post pst;
if (isQueryMultiple)
{
using (var multi = Connection.QueryMultiple(query.ToString(), new { Id = id }))
{
pst = multi.Read<Post, User, PostStatus, Post>((post, user, status) =>
{
if (post == null) return null;
if (user != null) post.Creator = user;
if (status != null) post.Status = status;
return post;
}).FirstOrDefault();
if (pst != null && validIncludesToPerform[2])
pst.Notes = multi.Read<PostNote>().ToList();
if (pst != null && validIncludesToPerform[3])
pst.PostTags = multi.Read<PostTag, Tag, PostTag>((pTag, tag) =>
{
if (pTag == null) return null;
if (tag != null) pTag.Tag = tag;
return pTag;
}).ToList();
}
}
else
{
pst = Connection.Query<Post, User, PostStatus, Post>
(query.ToString(), (post, user, status) =>
{
if (post == null) return null;
if (user != null) post.Creator = user;
if (status != null) post.Status = status;
return post;
}, new { Id = id }).FirstOrDefault();
}
重要说明: Dapper 假定您的 Id 列被命名为 "Id" 或 "id",如果您的主键不同或您想拆分"Id" 以外的点的宽行,使用可选的 'splitOn' 参数。这意味着在我的例子中我不必指定 "Id,Id" 因为这部分已经是隐式的。
- 处理
PostNotes
或PostTags
的部分工作正常。 - Post "including"
User
和Status
也工作正常。 - 如果 失败 当我尝试加载
User
"empty" 或仅使用User
或Status
...
错误信息: "When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id\r\nParameter name: splitOn"
原因:泛型的使用意味着查询将接收“3 个部分”,但它只是接收到一个较小的数字....
丑陋的解决方案:
我知道如果我只是将 Status
和 User
分成每个独立的查询,这会起作用:
if (validIncludesToPerform[0])
query.Append(@" select u.* from sys_user u right join test_post p on u.Id = p.CreatorId
where p.Id = @Id;");
if (validIncludesToPerform[1])
query.Append(@" select ps.* from test_poststatus ps right join test_post p on ps.Id = p.StatusId
where p.Id = @Id;");
但由于此信息是一对一的关系,我认为单个查询更好。所以,我尽量避免使用这个解决方案。
我也不想列出泛型签名的所有可能组合,例如:
Connection.Query<Post, User, PostStatus, Post>
Connection.Query<Post, User, Post>
Connection.Query<Post, PostStatus, Post>
Connection.Query<Post>
一旦我开始向这个 class 添加更多 1to1 关系,这种方法就不友好了。
我该如何解决这个问题?
您需要告诉 dapper 在何处拆分 - 对象之间的边界在您的 select statement.That 中是您的错误声明所说的位置。因此,将您的电话修改为:
pst = multi.Read<Post, User, PostStatus, Post>((post, user, status) =>
{
if (post == null) return null;
if (user != null) post.Creator = user;
if (status != null) post.Status = status;
return post;
}, splitOn: "Id,Id").FirstOrDefault();
split on 告诉 Dapper 在何处停止映射到一个对象并开始映射到另一个对象。由于您有 2 个联接,因此您需要拆分两个字段。确保将这两个放在一个由逗号分隔的字符串中,并且字符串中没有 space 个字符...
如需更好的解释,请参阅此 post:
Correct use of Multimapping in Dapper
我找到了一个 "somehow" 不太糟糕的方法:
var query = new StringBuilder();
query.Append(" select * from test_post p ");
query.Append(validIncludesToPerform[0]
? " left join sys_user u on u.Id = p.CreatorId "
: " left join sys_user u on 1 = 0 ");
query.Append(validIncludesToPerform[1]
? " left join test_poststatus s on s.Id = p.StatusId "
: " left join test_poststatus s on 1 = 0 ");
query.Append(" where p.Id = @Id; ");
我暂时留下这个答案,等待更好的选择....