[英]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.