简体   繁体   English

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

[英]What is a more efficient way to query MySQL using C#?

Based on links around the StackOverflow site (references below), I've come up with this block of code to perform queries from my C# application to a MySQL database. 基于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")));
            }
        }
    }
}

The problem I have with this, is that I have to build up this massive block of code every time I have a group of queries to make because I don't (and shouldn't) leave the connection open for the life of the application. 我遇到的问题是,每次我要进行一组查询时,我必须构建这个庞大的代码块,因为我不会(也不应该)在应用程序的生命周期中保持连接打开。

Is there a more efficient way to build the supporting structure (the nested using s, opening the connection, etc), and instead pass my connection string and the query I want to run and get the results back? 是否有更有效的方法来构建支持结构(嵌套using s,打开连接等),而是传递我的连接字符串和我想要运行的查询并获得结果?

Referenced questions: 参考问题:

That is three of the ones I looked at. 这是我看过的三个。 There were a few more, but my Google-fu can't refind them right now. 还有一些,但我的Google-fu现在无法重新启动它们。 All of these provide answers for how to perform a single query. 所有这些都为如何执行单个查询提供了答案。 I want to perform separate business logic queries - a few of them repeatedly - and don't want to repeat unneeded code. 我想执行单独的业务逻辑查询 - 其中一些重复 - 并且不想重复不需要的代码。

What I've tried: Based on the comment from nawfal, I have these two methods: 我尝试过:基于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;
}

The problem with this attempt is that the reader closes when it is returned from the method. 此尝试的问题是reader从方法返回时关闭。

Have you considered using an Object Relational Mapper (ORM) ? 您是否考虑过使用对象关系映射器(ORM) I'm fond of Castle Active Record and NHibernate myself, but there's plenty of others . 我自己喜欢Castle Active RecordNHibernate ,但还有很多其他人 Entity Framework and Linq to SQL are popular Microsoft solutions too. 实体框架Linq to SQL也是流行的Microsoft解决方案。

With these tools, your queries become pretty simple CRUD method calls that do the connection and session handling for you (mostly). 使用这些工具,您的查询变为非常简单的CRUD方法调用,可以为您(主要)进行连接和会话处理。

Instead of creating the reader in a using statement inside your RunSqlQuery method you could return it directly: 您可以直接返回它,而不是在RunSqlQuery方法中的using语句中创建reader:

return cmd.ExecuteReader();

Then wrap the call to RunSqlQuery in a using statement: 然后在using语句中包含对RunSqlQuery的调用:

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

You could use Action s or Func s to get what I think you are after. 你可以使用ActionFunc来获得我认为你想要的东西。

invoked like this... 像这样调用......

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

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

    if (!success)
        return false;

    return true;
}

implemented like this... 像这样实施......

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

Why do you want to return the datareader from the method? 为什么要从方法返回datareader? It will be closed once u wrap it in inside the using block. 一旦将其包裹在using块内,它将被关闭。 Also you can assign parameters only after getting an instance of IDbCommand , so I have moved that part to inside of the using block. 此外,您只能在获取IDbCommand的实例后分配参数,因此我已将该部分移动到using块的内部。

If you strictly want to return the datareader, then better return IEnumerable<IDataRecord> using the yield keyword. 如果您确实想要返回datareader,那么最好使用yield关键字返回IEnumerable<IDataRecord>

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

Or even better is to read the data there itself and return the data back, as in this question . 或者更好的是在那里读取数据并返回数据, 如本问题所示 That way you dont have to rely on classes in db namespaces outside your db class. 这样你就不必依赖db类之外的db命名空间中的类。

I have been down that road. 我一直走在那条路上。 Along the lines of suggesting ORMs, I would recommend EF Code First. 按照建议的ORM,我会推荐EF Code First。 Sorry to be a bit off topic, but I have never had a second thought about going back to this pattern after using EF Code First. 很抱歉有点偏离主题,但在使用EF Code First之后我再也没想过要回到这种模式。

Before Code First, EF was quite a pain, but now it has matured and if you had a DB you are potentially modifying structure, ie a new app feature requires a new table or column, then EF Code First approach is my recommendation. 在Code First之前,EF非常痛苦,但现在它已经成熟,如果你有一个DB你可能正在修改结构,即一个新的应用程序功能需要一个新的表或列,那么EF Code First方法是我的建议。 If it is a third party database or database for another app, that someone else manages its structure, then you only need to refresh your data model whenever they deploy changes, then I would not use Code First, and instead just use traditional EF where you generate/update your model based on some existing database. 如果它是另一个应用程序的第三方数据库或数据库,其他人管理其结构,那么只需要在部署更改时刷新数据模型,那么我就不会使用Code First,而只需使用传统的EF基于某些现有数据库生成/更新模型。

Note you could adopt EF and begin using it while you keep your existing code base as-is. 请注意,您可以采用EF并开始使用它,同时保持现有代码库的原样。 This depends on how much of your framework is dependent on using ADO objects though. 这取决于您的框架有多少依赖于使用ADO对象。 EF Power Tools extension has a way to generate a Code First model, or you could just use the traditional non-Code First EF to generate a modal from database. EF Power Tools扩展有一种生成Code First模型的方法,或者您可以使用传统的非Code First EF从数据库生成模态。

When you want to query, you can get right to the business of what you are trying to query without having alot of infrastructure code or wrappers. 当您想要查询时,您可以直接了解您尝试查询的内容,而无需使用大量基础结构代码或包装器。 The other thing about wrappers like the above, is there are edge cases that you will have to go back to using the ADO API instead of your RunSqlQuery helper. 关于像上面这样的包装器的另一件事是,有一些边缘情况你将不得不回到使用ADO API而不是RunSqlQuery帮助器。

This is a trivial example, as usually I don't have methods like GetActivePeopleNames, but just put the query where ever it is needed. 这是一个简单的例子,因为通常我没有像GetActivePeopleNames这样的方法,只是将查询放在需要的地方。 There is little overhead in terms of fluff code, so it isn't obtrusive to have my query among everything else. 在绒毛代码方面几乎没有开销,所以在其他所有内容中查询我并不突兀。 Although I do exercise some presenter patterns to abstract the query and data transformation from the business logic. 虽然我确实运用了一些演示者模式来从业务逻辑中抽象出查询和数据转换。

HREntities db = new HREntities();

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

I didn't have to create a parameter object. 我没有必要创建一个参数对象。 I could have used some variable for Where(p => p.IsActive == someBool) and it would have been safe from SQL injection in that context. 我可以Where(p => p.IsActive == someBool)使用一些变量,并且在该上下文中它可以安全地从SQL注入。 The connection is handled automatically. 连接自动处理。 I can use .Include to grab related objects in the same connection if needed. 如果需要,我可以使用.Include来抓取同一连接中的相关对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM