【问题标题】:C# SQLDataReader accessing by column nameC# SQLDataReader 按列名访问
【发布时间】:2021-12-24 17:57:56
【问题描述】:

我有一个与 MS SQL 数据库连接的 Visual Basic 应用程序。我有定义 SqlDataReader、打开连接并执行 ExecuteReader() 命令的代码。我使用以下代码从阅读器中检索数据

While myDataReader.Read()
    Session("menu_PEO") = myDataReader("menu_PEO")
    Session("menu_Transfer") = myDataReader("menu_Transfer")
    Session("menu_Loan") = myDataReader("menu_loan")
End While

menu_PEO、menu_Transfer 和 menu_loan 是 SQL 返回的数据中的 3 个列标题。

我现在的任务是将代码转换为 c#。我在 c# 中有以下代码有效:

while (dataReader.Read())
{
    dbMenuPEO = dataReader.GetString(1);
    dbMenuTransfer = dataReader.GetString(2);
    dbMenuLoan = dataReader.GetString(3);
}

由于我的 SQL 是 SELECT *,我无法保证返回数据的顺序,所以我不想依赖指定 GetString 的实例。

在 c# 中有没有一种方法可以指定我想要检索的列名,类似于它在 Visual Basic 中的工作方式?

谢谢!

【问题讨论】:

标签: c# sqldatareader


【解决方案1】:

有两种方法可以做到这一点。简单的方法是使用命名索引器:

dbMenuPEO = (string)dataReader["menu_PEO"];

本质上是一样的:

dbMenuPEO = (string)dataReader.GetValue(dataReader.GetOrdinal("menu_PEO"));

您可以将序数查找移到循环之外,这样效率更高。您还可以使用避免强制转换的强类型访问器。这在读取值类型(int、dateTime 等)时可能很重要,因为它避免了对值进行装箱,GetValue 会发生这种情况。

var menuPeoIdx = dataReader.GetOrdinal("menu_PEO");
while(dataReader.Read())
{
   dbMenuPEO = dataReader.GetString(menuPeoIdx);
}

通常,您应该永远在生产系统中使用select *。始终明确列出您想要的列。原因是它允许更改基础表,而无需修复使用代码或过度选择您不需要的列。 Select * 是“探索”数据集的绝佳工具,但只能用作开发工具。

【讨论】:

    【解决方案2】:

    我有定义 SqlDataReader、打开连接并执行 ExecuteReader() 的代码

    这不是必须编写的最令人难以置信的乏味代码吗?随着时间的推移,许多人都想到了这一点,并且发明了许多东西来减轻您的乏味。 MarkPflug 的回答直接解决了您的问题,但如果您不知道可以显着提高生产力,我想向您介绍其中一种技术

    在 c# 中是否有一种方法可以指定我想要检索的列名,类似于它在 Visual Basic 中的工作方式?

    这是一种方法,当你这样做时你不必输入它。它避免再次键入您已经键入的相同内容(两次 - 一次用于变量名,一次用于 SQL)

    使用Visual Studio内置的nuget package manager安装Dapper

    然后假设您有一个保存数据的类:

    //C#
    record DbMenu(string DbMenuPEO, string DbMenuTransfer, string DbMenuLoan);
    
    'or VB, if you like that sort of thing
    Class DbMenu
      Public Property DbMenuPEO as String
      Public Property DbMenuTransfer As String
      Public Property DbMenuLoan As String
    End Class
    

    您可以让 Dapper 进行查询、添加任何参数、打开您的连接、下载您的数据、填写一个完整的类列表、关闭连接并返回它......全部在一行代码中:

    //C#
    using var conn = ... //code here that gets a connection; doesn't need to be open
    var myListOfDbMenus = conn.Query<DbMenu>("SELECT * FROM ... ");
    
    'VB
    Using conn = ...
      Dim myListOfDbMenus = conn.Query(Of DbMenu)("SELECT * FROM ... ");
    End Using
    

    简短的版本是:您的 c# 类属性的名称应与列的名称相同。如果不是,最简单的方法是在 SQL 中使用 AS xyz 来均衡名称。如果要编写参数化查询,请提供@parameterNames,它与您在查询的同时传递的匿名对象的属性名称相同:

    var q = conn.Query<Type>("SELECT ... WHERE col = @val1", new {val1 = "hello" } );
    

    如果您喜欢编写 SQL 并拥有低级控制/不想使用像 EF 这样的 ORM,那么 Dapper 可让您继续直接执行 SQL,但会删除所有重复的周围样板

    【讨论】:

    • 免责声明:我与 Dapper 没有任何关系
    • 我在使用 Dapper 时没有问题,但我最初担心的是使用 SELECT * 时返回的值的顺序可能存在随机性。而且,我在我的开发中已经足够早了,我可以将 c# 类属性更改为与表格列标题相同。如果 Dapper 实际上确实将每一列加载到适当的类字段中,那么 Dapper 肯定会比手动输入每个实例更容易。
    • 确实如此;你可以有一个像record X(string A, string B, string C)这样的类和一个像Query&lt;..&gt;("SELECT C, B, A FROM X WHERE col1 = @val1 AND col2 = @val2", new { col2 = "x", col1 = "y" })这样的简洁调用,这一切都有效;顺序完全无关紧要。它还有更多功能,更新/插入/删除,查询单个对象,甚至将连接的结果集拆分为对象树......等等。推荐一个好的教程网站以获得更深入的建议,但它使对我来说很有意义
    • 将 c# 类属性更改为与表列标题相同 - 在 SQL 中使用 AS 很容易匹配它们,因此您可以保留 C#如果数据库不遵循约定(例如,如果您的 db_col_is_like_this 和您的 C# IsLikeThis)
    • 就像魔术一样! 1 行代码从数据库中读取 20 列并将数据加载到我的对象中!感谢您的建议!
    【解决方案3】:

    SQLDataReader 类公开了一组实例方法,允许检索特定类型字段的值,例如GetInt32() 和GetString(),可以与GetOrdinal() 方法结合使用

    例子

    dataReader.GetString(dataReader.GetOrdinal("YOUR FIELD NAME HERE"))
    

    【讨论】:

    • MarkPflug 的回答已经涵盖了这一点。请不要发布重复的建议
    • 我永远不会那样做。这会浪费大家的时间。事实上,当我开始输入我的答案时,只有你的答案——这确实表明了一种很好的方法,但并没有真正回答问题。当我提交我的时,马克的答案也在那里。所以,我们实际上是同时写的
    • 在这些情况下,您会打电话询问其他答案是否比您的答案更完整/信息更丰富,然后删除您的答案或将其扩展到竞争之外。如果真的没有其他东西可以添加(稀有)可以使您的答案明显更好的学习体验,并且这两个答案本质上是相同的,那么您指向“X 秒前发布”,看看谁先到了那里,如果你不是西部最快的枪,你点击删除.. 全力以赴,目的是保持如此干净的重复噪音
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-04
    • 1970-01-01
    • 2015-08-08
    • 2019-01-26
    • 1970-01-01
    相关资源
    最近更新 更多