[英]What is a more efficient way to query MySQL using C#?
基于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
从方法返回时关闭。
您是否考虑过使用对象关系映射器(ORM) ? 我自己喜欢Castle Active Record和NHibernate ,但还有很多其他人 。 实体框架和Linq to SQL也是流行的Microsoft解决方案。
使用这些工具,您的查询变为非常简单的CRUD方法调用,可以为您(主要)进行连接和会话处理。
您可以直接返回它,而不是在RunSqlQuery方法中的using语句中创建reader:
return cmd.ExecuteReader();
然后在using语句中包含对RunSqlQuery的调用:
using( var reader = RunSqlQuery(....) )
{
// Do stuff with reader.
}
像这样调用......
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;
}
}
为什么要从方法返回datareader? 一旦将其包裹在using
块内,它将被关闭。 此外,您只能在获取IDbCommand
的实例后分配参数,因此我已将该部分移动到using
块的内部。
如果您确实想要返回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;
}
}
}
或者更好的是在那里读取数据并返回数据, 如本问题所示 。 这样你就不必依赖db类之外的db命名空间中的类。
我一直走在那条路上。 按照建议的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来抓取同一连接中的相关对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.