获取第一列的值而不知道列名

4
类似于“列号而非列名”,但我认为两者不同,因为前者是针对未命名的列,而我的列是命名的,只是我事先不知道列的名称。
首先,一些背景。我正在创建一个方法,将查询及其参数作为参数,然后运行查询(使用Dapper),并检查第一行第一列的值是否为1或0。它旨在作为数据库的通用验证器工作。
例如,用户可以输入一个查询,如果某个表中存在某个值,则在仅有的行的唯一列中返回1,否则返回0。这种方法的使用范围故意非常广泛,大部分工作都放在用户编写查询上(我还应该澄清,这种情况下的“用户”始终是另一个开发人员,并且使用是在调用此方法的代码中完成的,而不是在表单输入中完成的)。
我想要查询我的数据库并获取第一行第一列的值,而不知道列的名称。理想情况下(虽然我不确定在Dapper调用中是否可能实现这一点),我还希望强制结果中只有一行和一列。当我搜索时,我基于帖子的第一个答案构建了我的初始解决方案,但发现为使其工作,我需要知道列的名称:
var dict = connection.Query(query, parameters).ToDictionary(row => (string)row.VALUE);
if(dict.ElementAt(0).Value != 1){
    //do stuff
}

我想过只需增加一个要求,让用户添加一个别名来命名第一列的某个固定值(在这种情况下,VALUE),但如果有可能,我宁愿不让用户承担这个负担。

我的核心问题是:如何在不知道列名的情况下,使用 Dapper 从数据库中获取命名列的值?有人有什么想法吗?


你如何定义结果集中的“第一”行?你所有的查询都保证有一个确定性的ORDER BY子句吗? - user5683823
最终,我希望查询只返回一行;这种方法是故意非常宽泛的。正如我所说,这种方法旨在成为数据库数据的通用验证器;因此,当正确使用时,查询将仅具有单个结果行。如果有多行,则查询无效,我可以在超出本问题范围的内容之外进行检查。这种方法的工作量落在编写查询的用户身上,这是有意的。如果可能的话,我只是不想要求他们命名列。 - Chris H.
3个回答

3

我只需在通用的Query方法中使用int作为类型,就可以成功完成此操作:

int value = conn.Query<int>(sql,
commandType: CommandType.StoredProcedure).FirstOrDefault();

请注意使用FirstOrDefault方法。这样你就可以从数据库中获取0或者任何其他值。


2
这确实是正确的方法。或者使用ExecuteScalar。 - Marc Gravell
只是为了确保我理解,如果由于任何原因查询返回的结果中第一行第一列的值不是整数,则“value”将被设置为0? - Chris H.
1
是的,我相信如此。当然,如果您不知道返回的类型,可以返回一个对象并在代码中检查类型。但这不是最好的想法。 - richinator38
“ExecuteScalar” 可能更快,但您失去了确保只有一行/列的能力。 - Jonathan Allen
作为对克里斯·H上面问题的更新,当我收到一个'null'值时,Dapper会抛出一个“对象引用未设置”的错误。因此我不得不将int改为int?,并确保它在设置之前具有一个值。 - richinator38

0
最简单的做法是通过别名确保 SQL 命令始终返回相同的列名称。这样,你的 C# 代码就可以直接使用该别名。

问题在于我想要接受用户输入的通用 SQL 命令,所以我不能保证别名会被使用。我在我的代码块后面提到了这一点。 - Chris H.
那么,你真的不应该使用Dapper或任何其他ORM!ORM旨在将您的代码与人为生成的SQL语句分离。它是一个很棒的工具,但你正在错误地使用它。 - Xavier J
这个方法仅供其他最熟悉 Dapper 并希望在查询中使用其参数映射功能进行验证的开发人员内部使用(该方法支持此功能)。您能详细说明一下为什么我不应该使用 Dapper 吗?我有点困惑。 - Chris H.
ORM通常用于将数据填充到具有固定属性名称的POCO类中,从预先确定的SQL语句中运行时之前。如果尝试使用Dapper从在运行时生成的SQL语句进行填充,则会破坏首次使用ORM的整个目的。 - Xavier J
Dapper 不会生成 SQL。 - Jonathan Allen
显示剩余2条评论

0
使用 Tortuga Chain
var result = DataSource.Sql(query, parameters).ToInt32().Execute();

我还想要求结果只有一行和一列。
默认情况下,Chain使用DbCommand.ExecuteScalar。它返回第一行的第一列,忽略其他所有内容。为了添加这个限制,我会这样做:
var result = DataSource.Sql(query, parameters).ToInt32List().Execute().Single;

另一个选择是改变生成SQL的方式。例如,

var filterObject = new {ColumnA = 1, ColumnB = "X", ColumnC = 17};
var result = DataSource.From(tableName, filterObject).ToInt32(columnName).Execute();

这样做的好处是可以免疫SQL注入攻击(这可能是您关心的问题),并确保在执行之前,您的SQL仅返回一列。


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