6

根据 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的 s、打开连接等),而是传递我的连接字符串和我想要运行的查询并返回结果?

参考问题:

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

4

5 回答 5

3

您是否考虑过使用对象关系映射器(ORM)?我自己喜欢Castle Active RecordNHibernate,但还有很多其他的。 Entity FrameworkLinq to SQL也是流行的 Microsoft 解决方案。

使用这些工具,您的查询变得非常简单的 CRUD 方法调用,为您(大部分)执行连接和会话处理。

于 2012-11-10T02:37:45.183 回答
2

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

return cmd.ExecuteReader();

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

using( var reader = RunSqlQuery(....) ) 
{        
  // Do stuff with reader.    
}
于 2012-11-12T06:09:42.270 回答
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;
    }
}
于 2012-11-12T19:03:00.613 回答
1

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

如果您严格要返回数据读取器,则最好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;
        }
    }
}

或者甚至更好的是读取那里的数据并返回数据,就像在这个问题中一样。这样,您就不必依赖 db 类之外的 db 命名空间中的类。

于 2012-11-12T04:40:51.500 回答
0

我一直在这条路上。按照建议 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 => p.IsActive == someBool)并且在那种情况下它不会受到 SQL 注入的影响。连接会自动处理。如果需要,我可以使用 .Include 来抓取同一连接中的相关对象。

于 2012-11-12T05:12:24.790 回答