【问题标题】:What is a more efficient way to query MySQL using C#?使用 C# 查询 MySQL 的更有效方法是什么?
【发布时间】:2012-10-30 09:30:11
【问题描述】:

根据 StackOverflow 站点周围的链接(以下参考资料),我想出了这段代码来执行从我的 C# 应用程序到 MySQL 数据库的查询。

using (var dbConn = new MySqlConnection(config.DatabaseConnection))
{
    using (var cmd = dbConn.CreateCommand())
    {
        dbConn.Open();
        cmd.CommandType = CommandType.Text;
        cmd.CommandText = "SELECT version() as Version";

        using (IDataReader reader = cmd.ExecuteReader())
        {
            if (reader.Read())
            {
                Console.WriteLine("Database Version: " + reader.GetString(reader.GetOrdinal("Version")));
            }
        }
    }
}

我遇到的问题是,每次我要进行一组查询时,我都必须构建这个庞大的代码块,因为我不(也不应该)终生保持连接打开的应用程序。

有没有更有效的方法来构建支持结构(嵌套的usings、打开连接等),而不是传递我的连接字符串和我想要运行的查询并返回结果?

参考问题:

这是我看过的三个。还有一些,但我的 Google-fu 现在无法重新找到它们。所有这些都为如何执行单个查询提供了答案。我想执行单独的业务逻辑查询——其中一些是重复的——并且不想重复不需要的代码。

我尝试过的: 根据nawfal的评论,我有这两种方法:

private MySqlDataReader RunSqlQuery(string query)
{
    Dictionary<string, string> queryParms = new Dictionary<string, string>();
    MySqlDataReader QueryResult = RunSqlQuery(query, queryParms);
    return QueryResult;
}

private MySqlDataReader RunSqlQuery(string query, Dictionary<string, string> queryParms)
{
    MySqlDataReader reader = null;
    if (queryParms.Count > 0)
    {
        // Assign parameters
    }

    try
    {
        using (var dbConn = new MySqlConnection(config.DatabaseConnection))
        {
            using (var cmd = dbConn.CreateCommand())
            {
                dbConn.Open();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = query;

                using (reader = cmd.ExecuteReader())
                {
                    return reader;
                }
            }
        }
    }
    catch (MySqlException ex)
    {
        // Oops.
    }

    return reader;
}

这个尝试的问题是reader 在从方法返回时关闭。

【问题讨论】:

  • 您可以使用自己的帮助类来执行一些简单的任务,这些样板代码只需编写一次。那很干。参考见stackoverflow.com/questions/13133804/…
  • @nawfal,我尝试编写一些样板代码。我将其添加到原始问题中。它与您链接的问题不同,并且有自己的问题 - 主要是读者在返回后关闭。
  • 弗兰克,阅读器已关闭,但只是因为已填充了整个数据。否则尝试yield return 方式,我已经在那篇文章中更新了..

标签: c# mysql datareader mysqldatareader


【解决方案1】:

您是否考虑过使用Object Relational Mapper (ORM)?我自己很喜欢Castle Active RecordNHibernate,但others 也很多。 Entity FrameworkLinq to SQL 也是流行的 Microsoft 解决方案。

使用这些工具,您的查询将变成非常简单的 CRUD 方法调用,为您(主要)进行连接和会话处理。

【讨论】:

  • 我没有考虑过使用 ORM。老实说,我一直希望有一些东西不会给应用程序增加太多东西以及我必须支持的东西。不过开始看起来我别无选择。
  • +1。我使用 EF,除了一些多对多关系的障碍(这对于所有 ORMS 来说似乎都很棘手),它很容易使用。
【解决方案2】:

您可以直接返回它,而不是在 RunSqlQuery 方法中的 using 语句中创建阅读器:

return cmd.ExecuteReader();

然后将对 RunSqlQuery 的调用包装在 using 语句中:

using( var reader = RunSqlQuery(....) ) 
{        
  // Do stuff with reader.    
}

【讨论】:

  • 嗯,我可能做错了,我不知道 c# 或 ADO.NET 在过去 4 年中是否发生了很大变化,但因为连接和命令是在返回时处理的,读者只是返回一个空的阅读器,并显示消息“阅读器关闭时尝试阅读无效”。
【解决方案3】:

你可以使用Actions 或Funcs 来获得我认为你想要的东西。

这样调用...

RunSqlQuery("SELECT * FROM ...", reader => ReadResult(reader));

private bool ReadResult(MySqlDataReader reader)
{
    //Use the reader to read the result

    if (!success)
        return false;

    return true;
}

这样实现...

private bool RunSqlQuery(string query, Func<MySqlDataReader, bool> readerAction)
{
    Dictionary<string, string> queryParms = new Dictionary<string, string>();
    return RunSqlQuery(query, readerAction, queryParms);
}

private bool RunSqlQuery(string query, Func<MySqlDataReader, bool> readerAction, Dictionary<string, string> queryParms)
{
    MySqlDataReader reader = null;
    if (queryParms.Count > 0)
    {
        // Assign parameters
    }

    try
    {
        using (var dbConn = new MySqlConnection(config.DatabaseConnection))
        {
            using (var cmd = dbConn.CreateCommand())
            {
                dbConn.Open();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = query;

                using (reader = cmd.ExecuteReader())
                {
                    return readerAction.Invoke(reader);
                }
            }
        }
    }
    catch (MySqlException ex)
    {
        // Oops.
        return false;
    }
}

【讨论】:

    【解决方案4】:

    为什么要从方法中返回数据读取器?一旦你将它包裹在using 块内,它将被关闭。此外,您只能在获得IDbCommand 的实例后才能分配参数,因此我已将该部分移至using 块的内部。

    如果您严格要返回数据读取器,那么最好使用 yield 关键字返回 IEnumerable&lt;IDataRecord&gt;

    private IEnumerable<IDataRecord> RunSqlQuery(string query, 
                                                 Dictionary<string, string> queryParms)
    {
        using (var dbConn = new MySqlConnection(config.DatabaseConnection))
        {
            using (var cmd = dbConn.CreateCommand())
            {
                if (queryParms.Count > 0)
                {
                    // Assign parameters
                }
                cmd.CommandText = query;
                cmd.Connection.Open();
                using (var reader = cmd.ExecuteReader())
                    foreach (IDataRecord record in reader as IEnumerable)
                        yield return record;
            }
        }
    }
    

    或者更好的是自己读取那里的数据并返回数据,as in this question。这样,您就不必依赖 db 类之外的 db 命名空间中的类。

    【讨论】:

      【解决方案5】:

      我一直在这条路上。按照建议 ORM 的思路,我会推荐 EF Code First。抱歉有点跑题了,但是在使用 EF Code First 之后,我从来没有想过要回到这种模式。

      在 Code First 之前,EF 相当痛苦,但现在它已经成熟,如果你有一个 DB,你可能会修改结构,即一个新的应用程序功能需要一个新的表或列,那么我推荐 EF Code First 方法.如果它是第三方数据库或另一个应用程序的数据库,由其他人管理其结构,那么您只需在他们部署更改时刷新您的数据模型,那么我不会使用 Code First,而只需使用传统的 EF根据一些现有的数据库生成/更新您的模型。

      请注意,您可以采用 EF 并在保持现有代码库不变的同时开始使用它。这取决于您的框架有多少依赖于使用 ADO 对象。 EF Power Tools 扩展有一种生成 Code First 模型的方法,或者您可以只使用传统的非 Code First EF 从数据库生成模式。

      当您想要查询时,您可以直接处理您尝试查询的业务,而无需使用大量基础架构代码或包装器。关于上述包装器的另一件事是,在某些极端情况下,您将不得不返回使用 ADO API 而不是 RunSqlQuery 帮助器。

      这是一个简单的例子,因为通常我没有像 GetActivePeopleNames 这样的方法,但只需将查询放在需要的地方。就绒毛代码而言,开销很小,因此将我的查询放在其他所有内容中并不突兀。虽然我确实练习了一些演示者模式来从业务逻辑中抽象出查询和数据转换。

      HREntities db = new HREntities();
      
      private ICollection<string> GetActivePeopleNames()
      {      
        return db.People.Where(p => p.IsActive).Select(p => p.FirstName + " " + p.LastName)
          .ToList();
      }
      

      我不必创建参数对象。我可以为Where(p =&gt; p.IsActive == someBool) 使用一些变量,并且在这种情况下它不会受到SQL 注入的影响。连接是自动处理的。如果需要,我可以使用 .Include 来抓取同一连接中的相关对象。

      【讨论】:

        猜你喜欢
        • 2013-02-08
        • 1970-01-01
        • 2023-03-29
        • 2011-03-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-19
        相关资源
        最近更新 更多