使用C#查询MySQL的更有效方法是什么?

时间:2012-11-10 02:22:42

标签: c# mysql datareader mysqldatareader

基于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")));
            }
        }
    }
}

我遇到的问题是,每次我要进行一组查询时,我必须建立这个庞大的代码块,因为我不会(也不应该)将连接打开申请。

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

参考问题:

这是我看过的三个。还有一些,但我的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在从方法返回时关闭。

5 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:2)

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

return cmd.ExecuteReader();

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

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

答案 2 :(得分:2)

您可以使用ActionFunc来获取我认为您所追求的内容。

这样调用......

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;
    }
}

答案 3 :(得分:1)

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

如果您严格要退回datareader,请使用IEnumerable<IDataRecord>关键字更好地返回yield

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命名空间中的类。

答案 4 :(得分:0)

我一直走在那条路上。按照建议的ORM,我会推荐EF Code First。很抱歉有点偏离主题,但在使用EF Code First之后我再也没想过要回到这种模式。

在Code First之前,EF非常痛苦,但现在它已经成熟,如果你有一个数据库你可能正在修改结构,即一个新的应用程序功能需要一个新的表或列,那么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 => p.IsActive == someBool)使用一些变量,并且在该上下文中它可以安全地从SQL注入。连接自动处理。如果需要,我可以使用.Include来抓取同一连接中的相关对象。