从C#获取SQL数据的最佳方法是什么?

6

我正在尝试找到最佳的方式(速度与易用性)通过C#代码访问SQL Server代码。

在学习书籍时,我遇到了多种建议,通常是告诉我通过拖放来完成。然而,由于我想要采用代码方式,因此首先尝试通过列号获取数据,但是在SQL查询中进行任何重新排序(例如添加/删除列)都很难修复。

例如(不要笑,有些代码已经两年了),我甚至编写了特殊函数来传递sqlQueryResult并检查它是否为空:

public static void exampleByColumnNumber(string varValue) {

        string preparedCommand = @"SELECT TOP 1 [SomeColumn],[SomeColumn2]

                                  FROM [Database].[dbo].[Table]
                  WHERE [SomeOtherColumn] = @varValue";
        SqlCommand sqlQuery = new SqlCommand(preparedCommand, Locale.sqlDataConnection);
        sqlQuery.Prepare();
        sqlQuery.Parameters.AddWithValue("@varValue) ", varValue);

        SqlDataReader sqlQueryResult = sqlQuery.ExecuteReader();
        if (sqlQueryResult != null) {
            while (sqlQueryResult.Read()) {
                string var1 = Locale.checkForNullReturnString(sqlQueryResult, 0);
            string var2 = Locale.checkForNullReturnString(sqlQueryResult, 1);
            }
            sqlQueryResult.Close();
        }
    }

后来我发现可以通过列名实现(如果有多个列和大量更改顺序等,这似乎更容易阅读):

    public static void exampleByColumnNames(string varValue) {

        string preparedCommand = @"SELECT TOP 1 [SomeColumn],[SomeColumn2]

                                  FROM [Database].[dbo].[Table]
                  WHERE [SomeOtherColumn] = @varValue";
        SqlCommand sqlQuery = new SqlCommand(preparedCommand, Locale.sqlDataConnection);
        sqlQuery.Prepare();
        sqlQuery.Parameters.AddWithValue("@varValue) ", varValue);

        SqlDataReader sqlQueryResult = sqlQuery.ExecuteReader();
        if (sqlQueryResult != null) {
            while (sqlQueryResult.Read()) {
                string var1 = (string) sqlQueryResult["SomeColumn"];
            string var2 = (string) sqlQueryResult["SomeColumn2"];
            }
            sqlQueryResult.Close();
        }
    }

第三个示例是通过使用列名进行操作,但使用.ToString()来确保它不是空值,或者通过对空值进行If/else检查。

    public static void exampleByColumnNamesAgain(string varValue) {

        string preparedCommand = @"SELECT TOP 1 [SomeColumn],[SomeColumn2], [SomeColumn3]

                                  FROM [Database].[dbo].[Table]
                  WHERE [SomeOtherColumn] = @varValue";
        SqlCommand sqlQuery = new SqlCommand(preparedCommand, Locale.sqlDataConnection);
        sqlQuery.Prepare();
        sqlQuery.Parameters.AddWithValue("@varValue) ", varValue);

        SqlDataReader sqlQueryResult = sqlQuery.ExecuteReader();
        if (sqlQueryResult != null) {
            while (sqlQueryResult.Read()) {
                string var1 = (string) sqlQueryResult["SomeColumn"].ToString();
            DateTime var2;
        DateTime.TryParse(sqlQueryResult["SomeColumn2"].ToString());

        int varInt = ((int) sqlQueryResult["SomeColumn3"] == null ? 0 : (int) sqlQueryResult["SomeColumn3"];

            }
            sqlQueryResult.Close();
        }
    }

请注意,我只是为了这个例子而创建的,可能会有一些拼写错误或轻微的语法错误,但主要问题是哪种方法最好,哪种最差(我知道第一个是我最不喜欢的)。我很快就要开始重写我的小型90k行应用程序的某些部分,其中至少有这3个广泛使用的示例,因此我希望能够获得最快速且最易于维护的最佳方法(希望它将是相同的方法)。也许有更好的选择,请分享?

1
你说“过早优化”? - mjv
1
大致意思是“在完成之前不要担心性能问题”。你现在还不知道数据库访问是否会成为瓶颈,所以暂时不要担心性能。如果你编写易读、易理解、易维护的代码,你将能更快地完成工作,并减少错误。你随时可以回来优化性能。 - Iain Galloway
问题在于我发现我的 SQL 在大多数情况下似乎成为了瓶颈。例如,我正在从 SQL 中获取一些 int 值,并将其放入 C# 循环中,以获取 ArrayList 中每个客户端的 int 值。我可以看到处理器最多只有 2-10% 的使用率,网络连接也只有 2% 的使用率。所以我开始担心出了什么问题。 - MadBoy
当然,如果你有10,000个客户,并且你为每个客户进行单独的SQL查询,那么你将在数据库中花费很长时间。为什么不一次性查询所有10,000个客户,而不用担心按索引或列名提取数据是否更快? - Iain Galloway
就说吧,我的C#知识比SQL好得多。我正在慢慢学习SQL编程,但有些任务现在对我来说太难了。例如,我曾经尝试通过WHERE [Variable] IN (1,2,3.....)传递参数,并尝试将其作为一个@var传递。但它没有起作用,出现了一些奇怪的注释导致崩溃。所以我使用了for循环,现在它可以工作了 :) 我在工作中是孤军奋战,没有其他IT人员,也没有同事程序员,所以当我整个生命都是管理员,现在我要做所有的事情,这真的很困难 :-) - MadBoy
显示剩余2条评论
7个回答

7

看起来你可能在查看旧书。如果你要用“老派”的方式,那么至少应该使用using块。摘要:

using (var connection = new SqlConnection(connectionString))
{
    using (var command = new SqlCommand(commandString, connection))
    {
        using (var reader = command.ExecuteReader())
        {
             // Use the reader
        }
    }
}

更好的选择是了解Entity Framework。链接:Entity Framework。 相关链接:数据开发中心

我的问题更多关于使用正确的列名方式与列编号以及不同解决方案。虽然你的代码给了我一些新的提示,但并没有完全回答我最初的问题 :-) - MadBoy
@MadBoy:实际上,我回答的其余部分已经解决了你的问题。使用Entity Framework,你的问题就不会出现。你只需要使用强类型属性:var q = from c in context select c; foreach (var r in q) {// r.SomeColumn; r.SomeColumn2;} - John Saunders
@John Saunders - +1个答案,但是没有必要使用所有的{}嵌套;您可以堆叠using行,只有一个嵌套级别。在一个小例子中,这并不是真正的问题(除了口味选择),但是在大块代码中,它确实可以提高可读性。 - stevehipwell
1
嗯,Entity Framework(或任何其他ORM),并不是万能的解决方案。我确实同意它是某些问题的非常有用的工具,但我不会称其为“处理数据的新方式”(它甚至不是在处理数据,而是在处理实体)。 并不是所有应用程序都需要将关系模型转换为对象模型。 - Frederik Gheysels
当开发一个有很多业务逻辑的应用程序时,我会使用像NHibernate或EF这样的ORM。 当开发一个真正以数据为中心,没有太多BL的应用程序时,我认为我会使用数据集。 - Frederik Gheysels
1
重要的是要记住,EF在追求开发速度的同时牺牲了应用程序的性能。对于每个应用程序,您必须决定哪个更为重要。没有标准答案。我曾经在大型EF软件系统上工作过,我们可能不得不开始转换为使用SqlClient以解决性能问题。 - Onkel-j

2

您应该查看这些教程,

[http://www.asp.net/learn/data-access/][1]

您计划的所有工作已经完成。

请看这种做法与您正在做的相同

  string preparedCommand =
  @"SELECT TOP 1 [SomeColumn],[SomeColumn2], [SomeColumn3]    
  FROM [Database].[dbo].[Table]
  WHERE [SomeOtherColumn] = @varValue";
  [1]: http://www.asp.net/learn/data-access/

更好的方法是使用LINQ TO SQL来完成上述任务。
var result = from someObject in SomeTable
             where SomeColumnHasValue == ValueToCompare
             select new { SomeColumn, SomeColumn1, SomeColumn2};
  • 无类型安全问题
  • 在使用C#工作时可以可视化数据库
  • 编译时错误较少
  • 代码较少
  • 更高的生产效率

以下是一些如果您感兴趣的LINQ资源

希望对您有所帮助


不确定您所说的“完成并自己使用”是什么意思。我已经编写了它并且它可以工作。但由于我同时在学习C#/SQL,所以我想它可能有很多糟糕的代码,迟早我会想要修复它们,特别是因为我需要在这里和那里添加/改进一些代码,所以我也可以更新一些部分到更好的新代码。 - MadBoy
我很快就要开始/重写我的小型90k行应用程序的一些部分,这至少有那3个经常使用的示例。你是对的,我理解得不正确,我以为你要开始处理它。无论如何,祝你好运,并希望你在替换旧应用程序的某些部分时享受使用新的数据访问方法。 - Asad

2

是的,数据库已经运行并且人们正在使用我的应用程序。但是由于我必须继续开发(并在一些我去年最后一次停留的地方进行改进),我知道代码必须在某些地方进行大量重写,特别是因为我希望它是多线程的。 - MadBoy
你将能够开始运行,但却会受限于你的数据库当前结构。LINQ to SQL 总是在数据库表和类之间产生一对一的映射关系。如果你的数据库发生变化,那么你的类也将不得不改变。Entity Framework 解决了这个问题,并且不仅限于与 SQL Server 一起使用。 - John Saunders
当然。另一方面,EF 的学习曲线要陡峭得多。你需要几分钟而不是几秒钟的时间来才能运行 :P - Iain Galloway

1
不要仅将数据转换为字符串以尝试解析它;DataReaders 有方法可以将 SQL 数据转换为 .Net 数据类型:
using (var connection = new SqlConnection(Locale.sqlDataConnection))
using (var command = new SqlCommand(preparedCommand, connection))
using (var reader = command.ExecuteReader())
{
    int stringColumnOrdinal = reader.GetOrdinal("SomeColumn");
    int dateColumnOrdinal = reader.GetOrdinal("SomeColumn2");
    int nullableIntColumnOrdinal = reader.GetOrdinal("SomeColumn3");
    while (reader.Read())
    {
        string var1 = reader.GetString(stringColumnOrdinal);
        DateTime var2 = reader.GetDateTime(dateColumnOrdinal);
        int? var3 = reader.IsDBNull(nullableIntColumnOrdinal) ? null : (int?)reader.GetInt32(nullableIntColumnOrdinal);
    }
}

在C#中,整数类型不能为NULL。应该使用以下代码: int? var3 = reader.IsDBNull(nullableIntColumnOrdinal) ? 0 : reader.GetInt32(nullableIntColumnOrdinal); - user1853517
@Chizl,我不是在创建一个空整数,而是在创建一个空的int?。不要将空值转换为零,否则您无法区分缺失/空值和零值。 - Dour High Arch
在VS2010中,它将无法编译 int? var3 = reader.IsDBNull(1) ? null : reader.GetInt32(1); 。仅将其粘贴到VS中会使“null : reader.GetInt32(1)”下面出现读取下划线。 - user1853517
哦,我现在明白了;那是因为代码从一个更大的示例中被修剪了,而我犯了一个错别字。谢谢你注意到了。 - Dour High Arch

1

如果你想使用纯粹的ADO.net,你可能需要去找微软企业库的数据访问应用程序块。David Hayden有一篇不错的文章详细介绍了如何使用它。

祝你好运,希望这能对你有所帮助。


1
在C#中进行数据访问的最简单方法,我认为是使用类型化的DataSets。其中很多都是拖放式的操作,在.NET 2.0+中甚至比.NET 1.0/1.1更容易。
请看一下这篇文章,讲解了如何使用类型化的DataSets和TableAdapters: Building a DAL using Strongly Typed TableAdapters and DataTables in VS 2005 and ASP.NET 2.0 类型化的DataSet基本上是您数据的容器。您使用TableAdapter来填充它(使用SQL或存储过程,任选其一),并在之后更新数据。在您的DataSet中,每个DataTable中的列名都是从用于填充它们的SQL自动生成的;数据库表之间的关系会被反映在DataSet中各个DataTable之间的关系中。

0

我测试了许多不同的方法,以从SQL Server数据库中获取数据,并发现最快的方法如下:

首先,根据您所需的属性创建具有“IDataRecord”参数化方法的类。

       class emp
        {
            public int empid { get; set; }
            public string name { get; set; }
            public static emp create(IDataRecord record)
            {
                return new emp
                {
                    empid = Convert.ToInt32(record["Pk_HotelId"]),
                    name = record["HotelName"].ToString()
                };
            }
        }

现在创建获取数据的方法如下:
public List<S> GetData<S>(string query, Func<IDataRecord, S> selector)
        {
            using (var cmd = conn.CreateCommand())
            {
                cmd.CommandText = query;
                cmd.Connection.Open();
                using (var r = cmd.ExecuteReader())
                {
                    var items = new List<S>();
                    while (r.Read())
                        items.Add(selector(r));
                    return items;
                }
            }
        }

然后调用函数,如下:

var data = GetData<emp>("select * from employeeMaster", emp.create);

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