本文会实现数据查询组件,示例主要使用MySQL数据库,相关准备知识可以参考如下资源:
《MySQL数据库应用》合集,http://caohuayu.com/article/Group.aspx?id=3
《数据查询》文章,http://caohuayu.com/article/Article.aspx?id=a261005
tQuery接口定义了数据查询的组件标准,代码如下(cfx/data/tQuery.cs)。
using System.Collections.Generic; using System.Data; namespace cfx.data { public interface tQuery { string CnnStr { get; } string Source { get; } // tQuery SetCond(tCond cond); tQuery SetCond(string condSql); tQuery SetLimit(long limit); tQuery SetField(string field); tQuery AddOrderBy(string field, bool isAscending = true); // tPair GetValue(); tPairList GetRow(); DataTable GetTable(); List<double> GetDblCol(); List<string> GetStrCol(); } // 其它代码 }
tQuery接口中,首先定义了CnnStr和Source两个只读属性,分别用于返回数据库连接字符串和查询数据源名称(如表、视图等)。
接下来是设置查询要素的方法,包括:
执行查询并返回查询结果的方法有五个,分别是:
tQueryBase是实现tQuery接口组件的基类,定义如下(cfx/data/tQuery.cs):
using System.Collections.Generic; using System.Data; namespace cfx.data { // 其它代码 // public abstract class tQueryBase : tQuery { protected string myCnnStr; protected string mySource; protected tCond myCond = null; protected long myLimit = -1; protected List<string> myField = new List<string> { "*" }; protected tPairList myOrderBy = tPairList.Create(); // public tQueryBase(string cnnstr,string source) { myCnnStr = cnnstr; mySource = source; } // public string CnnStr { get { return myCnnStr; } } public string Source { get { return mySource; } } // public tQuery SetCond(tCond cond) { myCond = cond; return this; } // public tQuery SetCond(string condSql) { myCond = tCond.Direct(condSql); return this; } // public tQuery SetLimit(long limit) { myLimit = limit; return this; } // public tQuery SetField(string field) { if (field == null) return this; myField = field.ReSplit(@"\s*,\s*"); return this; } // public tQuery AddOrderBy(string field, bool isAscending = true) { myOrderBy.Add(field, isAscending ? "asc" : "desc"); return this; } // public abstract tPair GetValue(); public abstract tPairList GetRow(); public abstract DataTable GetTable(); public abstract List<double> GetDblCol(); public abstract List<string> GetStrCol(); } }
tQueryBase类中,首先定义了几个字段,用于保存内部数据,分别是:
接下来,构造函数的参数需要指定数据库连接字符串和查询数据源。一系列的Set方法会将设置的查询要素保存到对应的字段中;AddOrderBy()方法可以多次调用添加多个排序字段和排序方法,并保存到myOrderBy对象,元素(tPair类型)的Name属性保存排序字段名,Value属性保存排序方法关键字,如asc(升序,默认值)和desc(降序)。
执行查询的方法则定义为抽象类,需要在子类中实现。
下面的代码(cfx/data/mysql/tMySqlQuery.cs)是tMySqlQuery类的实现,用于MySQL数据库的数据查询操作。
using System.Collections.Generic; using System.Data; using System.Text; using MySql.Data.MySqlClient; namespace cfx.data.mysql { public class tMySqlQuery : tQueryBase { public tMySqlQuery(string cnnstr, string source) : base(cnnstr, source) { } // public static tMySqlQuery Create(string cnnstr,string source) { return new tMySqlQuery(cnnstr, source); } // 创建select语句 protected string GetSelectSql(out List<object> condArg) { string condSql = tMySqlCondBuilder.Get(myCond, out condArg); // if (myCnnStr == null || myCnnStr.Length == 0 || mySource == null || mySource.Length == 0) return ""; // StringBuilder sb = new StringBuilder("select", 512); // 返回字段 if(myField.Count==0 || myField[0] == "*") { sb.Append(" *"); } else { sb.AppendFormat(" `{0}`", myField[0]); for (int i = 1; i < myField.Count; i++) sb.AppendFormat(",`{0}`", myField[i]); } // 数据源 sb.AppendFormat(" from `{0}`", mySource); // 条件 if (condSql.Length > 0) sb.AppendFormat(" where {0}", condSql); // 排序字段 if (myOrderBy.Count > 0) { sb.AppendFormat(" order by `{0}` {1}", myOrderBy[0].Name, myOrderBy[0].Value); for (int i = 1; i < myOrderBy.Count; i++) sb.AppendFormat(",`{0}` {1}", myOrderBy[i].Name, myOrderBy[i].Value); } // 返回记录数量 if (myLimit > 0) sb.AppendFormat(" limit {0}", myLimit); // return sb.ToString(); } // public override tPair GetValue() { try { List<object> condArg; string sql = GetSelectSql(out condArg); if (sql.Length == 0) return tPair.Create(); // using(MySqlConnection cnn = new MySqlConnection(myCnnStr)) { cnn.Open(); MySqlCommand cmd = cnn.CreateCommand(); cmd.CommandText = sql; for (int i = 0; i < condArg.Count; i++) cmd.Parameters.AddWithValue("?cond" + i, condArg[i]); return tPair.Create("", cmd.ExecuteScalarAsync().Result); } } catch { return tPair.Create(); } } // public override tPairList GetRow() { try { List<object> condArg; string sql = GetSelectSql(out condArg); if (sql.Length == 0) return tPairList.Create(); // using (MySqlConnection cnn = new MySqlConnection(myCnnStr)) { cnn.Open(); MySqlCommand cmd = cnn.CreateCommand(); cmd.CommandText = sql; for (int i = 0; i < condArg.Count; i++) cmd.Parameters.AddWithValue("?cond" + i, condArg[i]); using (MySqlDataReader dr = cmd.ExecuteReaderAsync().Result as MySqlDataReader) { tPairList pl = tPairList.Create(); if (dr.Read()) { for (int col = 0; col < dr.FieldCount; col++) pl.Add(dr.GetName(col), dr[col]); } return pl; } } } catch { return tPairList.Create(); } } // public override DataTable GetTable() { try { List<object> condArg; string sql = GetSelectSql(out condArg); if (sql.Length == 0) return null; // using (MySqlConnection cnn = new MySqlConnection(myCnnStr)) { cnn.Open(); MySqlCommand cmd = cnn.CreateCommand(); cmd.CommandText = sql; for (int i = 0; i < condArg.Count; i++) cmd.Parameters.AddWithValue("?cond" + i, condArg[i]); using (MySqlDataAdapter ada = new MySqlDataAdapter(cmd)) { DataSet ds = new DataSet(); if (ada.Fill(ds) > 0) return ds.Tables[0]; else return null; } } } catch { return null; } } // public override List<double> GetDblCol() { try { List<object> condArg; string sql = GetSelectSql(out condArg); if (sql.Length == 0) return new List<double>(); // using (MySqlConnection cnn = new MySqlConnection(myCnnStr)) { cnn.Open(); MySqlCommand cmd = cnn.CreateCommand(); cmd.CommandText = sql; for (int i = 0; i < condArg.Count; i++) cmd.Parameters.AddWithValue("?cond" + i, condArg[i]); using (MySqlDataReader dr = cmd.ExecuteReaderAsync().Result as MySqlDataReader) { List<double> lst = new List<double>(); while (dr.Read()) lst.Add(dr.GetDouble(0)); return lst; } } } catch { return new List<double>(); } } // public override List<string> GetStrCol() { try { List<object> condArg; string sql = GetSelectSql(out condArg); if (sql.Length == 0) return new List<string>(); // using (MySqlConnection cnn = new MySqlConnection(myCnnStr)) { cnn.Open(); MySqlCommand cmd = cnn.CreateCommand(); cmd.CommandText = sql; for (int i = 0; i < condArg.Count; i++) cmd.Parameters.AddWithValue("?cond" + i, condArg[i]); using (MySqlDataReader dr = cmd.ExecuteReaderAsync().Result as MySqlDataReader) { List<string> lst = new List<string>(); while (dr.Read()) lst.Add(dr.GetString(0)); return lst; } } } catch { return new List<string>(); } } // } }
tMySqlQuery类继承于tQueryBase类,构造函数和Create()静态方法用于创建实例,需要指定数据库连接字符串和查询数据源名称。
GetSelectSql()方法 用于创建select语句。方法中,首先生成了查询条件语句并使用condArg参数输出条件中的参数数据;应注意,这里支持无条件查询,如果不允许无条件查询,可以在第一个if语句的条件中加上condSql.Length==0。接下来,如果没有指定返回字段或指定为"*",则在select语句使用*符号设置返回数据源中的所有字段。然后,依次组合查询数据源、查询条件、排序字段、返回记录数量;最后,返回组合完成的select语句。
执行查询的方法代码并不难,其中需要使用MySqlDataReader对象读取查询数据,以及MySqlDataAdapter和DataSet类的使用,下面分别介绍。
MySqlDataReader类基于DbDataReader,用于读取查询返回的数据,下面是一些常用成员。
HasRows属性,判断是否包含查询结果,包含记录时返回true,否则返回false。
FieldCount属性,返回查询结果的列数。
Read()方法,读取下一行数据,第一次调用时会指向第一行数据。成功读取时返回true,否则返回false,可以根据返回结果判断当前行是否有可用的数据。
GetName(columnIndex)方法,根据列的数值索引返回字段名。
this[columnIndex]方法,根据列的数值索引返回当前行指定字段的数据,返回类型为object。
GetDouble(columnIndex)方法,根据列的数值索引返回对应列的double类型数据。
GetString(columnIndex)方法,根据列的数值索引值返回对应列的string类型数据。
MySqlDataReader类中还可以使用一系列GetXXX()方法读取指定列的不同类型的数据,方法名中的XXX是.NET Framework中定义的数据类型名称。
示例中,MySqlDataAdapter类使用MySqlCommand对象作为构造函数的参数,并使用Fill()方法将查询结果填充到DataSet对象;Fill()方法会返回填充的记录数量,示例中,当查询结果包含数据时则返回DataSet对象中的第一个表(DataTable类型),使用Tables[0]获取;这里,DataSet对象的Tables属性包含了对象中的数据表集合。
在HeidiSQL中执行如下代码,以创建MySQL数据库的查询测试数据。
use cdb_cs1; insert into t1(f1,f2,f3,f4) values('user01',0,'2025-10-11 11:00:00','111'), ('user02',1,'2025-11-05 10:30:00','111'), ('user03',1,'2025-11-06 10:15:00','111.001'), ('user04',2,'2026-01-16 11:30:00','111.001'), ('user05',2,'2026-01-19 09:30:00','111.001'), ('user06',1,'2026-02-28 10:30:00','111.002'), ('user07',2,'2026-03-11 11:30:00','111.002'), ('user08',1,'2026-03-13 10:25:00','112'), ('user09',2,'2026-03-19 15:35:00','112.001'), ('user10',1,'2026-03-21 10:15:00','112.002');
执行代码会在cdb_cs1数据库中的t1表中添加10条记录。
下面的代码,我们在Program.cs文件中测试tMySqlQuery类的应用。
using System; using cfx.data; using cfx.data.mysql; namespace csfx_demo { class Program { static void Main(string[] args) { string cnnstr = tMySqlHelper.GetCnnStr("127.0.0.1", "cdb_cs1", "root", "DEV_Test123456", 3306); tPair result = tMySqlQuery.Create(cnnstr, "t1"). SetField("f1").SetCond(tCond.Equal("f1", "user01")) .GetValue(); Console.WriteLine(result.Value); } } }
执行代码会显示user01。
下面的代码会读取一行数据。
using System; using cfx.data; using cfx.data.mysql; namespace csfx_demo { class Program { static void Main(string[] args) { string cnnstr = tMySqlHelper.GetCnnStr("127.0.0.1", "cdb_cs1", "root", "DEV_Test123456", 3306); tPairList result = tMySqlQuery.Create(cnnstr, "t1"). SetField("f1,f2,f3,f4").SetCond(tCond.Equal("f1", "user01")) .GetRow(); // for(int i=0; i < result.Count; i++) { Console.WriteLine("{0} : {1}", result[i].Name, result[i].Value); } } } }
执行代码显示结果如下图所示。
下面的代码会查询f2字段等于1的记录,并分行显示。
using System; using cfx.data; using cfx.data.mysql; using System.Data; namespace csfx_demo { class Program { static void Main(string[] args) { string cnnstr = tMySqlHelper.GetCnnStr("127.0.0.1", "cdb_cs1", "root", "DEV_Test123456", 3306); DataTable result = tMySqlQuery.Create(cnnstr, "t1"). SetField("f1,f2,f3,f4").SetCond(tCond.Equal("f2",1)) .GetTable(); // Console.WriteLine("f1\tf2\tf3\tf4"); for (int row=0; row < result.Rows.Count; row++) { Console.WriteLine("{0}\t{1}\t{2}\t{3}", result.Rows[row][0], result.Rows[row][1], result.Rows[row][2], result.Rows[row][3]); } } } }
显示结果如下图所示。
下面的代码会读取f2列的所有数据。
using System; using cfx.data; using cfx.data.mysql; using System.Collections.Generic; namespace csfx_demo { class Program { static void Main(string[] args) { string cnnstr = tMySqlHelper.GetCnnStr("127.0.0.1", "cdb_cs1", "root", "DEV_Test123456", 3306); List<double> result = tMySqlQuery.Create(cnnstr, "t1"). SetField("f2").GetDblCol(); // for (int i=0;i<result.Count;i++) { Console.WriteLine(result[i]); } } } }
下面的代码会按recid字段降序显示数据。
using System; using cfx.data; using cfx.data.mysql; using System.Data; namespace csfx_demo { class Program { static void Main(string[] args) { string cnnstr = tMySqlHelper.GetCnnStr("127.0.0.1", "cdb_cs1", "root", "DEV_Test123456", 3306); DataTable result = tMySqlQuery.Create(cnnstr, "t1"). SetField("recid,f1,f2,f3,f4").AddOrderBy("recid",false) .GetTable(); // Console.WriteLine("recid\tf1\tf2\tf3\tf4"); for (int row = 0; row < result.Rows.Count; row++) { Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}", result.Rows[row][0], result.Rows[row][1], result.Rows[row][2], result.Rows[row][3], result.Rows[row][4]); } } } }
执行结果如下图所示。
下面的代码(cfx/data/sql/tSqlQuery.cs),使用tSqlQuery类实现SQL Server数据库数据查询操作。
using System.Collections.Generic; using System.Text; using System.Data; using System.Data.SqlClient; namespace cfx.data.sql { public class tSqlQuery : tQueryBase { public tSqlQuery(string cnnstr, string source) : base(cnnstr, source) { } // public static tSqlQuery Create(string cnnstr,string source) { return new tSqlQuery(cnnstr, source); } // 创建select语句 protected string GetSelectSql(out List<object> condArg) { string condSql = tSqlCondBuilder.Get(myCond, out condArg); // if (myCnnStr == null || myCnnStr.Length == 0 || mySource == null || mySource.Length == 0) return ""; // StringBuilder sb = new StringBuilder("select", 512); // 返回记录数量 if (myLimit > 0) sb.AppendFormat(" top {0}", myLimit); // 返回字段 if (myField.Count==0 || myField[0] == "*") { sb.Append(" *"); } else { sb.AppendFormat(" [{0}]", myField[0]); for (int i = 1; i < myField.Count; i++) sb.AppendFormat(",[{0}]", myField[i]); } // 数据源 sb.AppendFormat(" from [{0}]", mySource); // 条件 if (condSql.Length > 0) sb.AppendFormat(" where {0}", condSql); // 排序字段 if (myOrderBy.Count > 0) { sb.AppendFormat(" order by [{0}] {1}", myOrderBy[0].Name, myOrderBy[0].Value); for (int i = 1; i < myOrderBy.Count; i++) sb.AppendFormat(",[{0}] {1}", myOrderBy[i].Name, myOrderBy[i].Value); } // return sb.ToString(); } // public override tPair GetValue() { try { List<object> condArg; string sql = GetSelectSql(out condArg); if (sql.Length == 0) return tPair.Create(); // using(SqlConnection cnn = new SqlConnection(myCnnStr)) { cnn.Open(); SqlCommand cmd = cnn.CreateCommand(); cmd.CommandText = sql; for (int i = 0; i < condArg.Count; i++) cmd.Parameters.AddWithValue("@cond" + i, condArg[i]); return tPair.Create("", cmd.ExecuteScalarAsync().Result); } } catch { return tPair.Create(); } } // public override tPairList GetRow() { try { List<object> condArg; string sql = GetSelectSql(out condArg); if (sql.Length == 0) return tPairList.Create(); // using (SqlConnection cnn = new SqlConnection(myCnnStr)) { cnn.Open(); SqlCommand cmd = cnn.CreateCommand(); cmd.CommandText = sql; for (int i = 0; i < condArg.Count; i++) cmd.Parameters.AddWithValue("@cond" + i, condArg[i]); using (SqlDataReader dr = cmd.ExecuteReaderAsync().Result as SqlDataReader) { tPairList pl = tPairList.Create(); if (dr.Read()) { for (int col = 0; col < dr.FieldCount; col++) pl.Add(dr.GetName(col), dr[col]); } return pl; } } } catch { return tPairList.Create(); } } // public override DataTable GetTable() { try { List<object> condArg; string sql = GetSelectSql(out condArg); if (sql.Length == 0) return null; // using (SqlConnection cnn = new SqlConnection(myCnnStr)) { cnn.Open(); SqlCommand cmd = cnn.CreateCommand(); cmd.CommandText = sql; for (int i = 0; i < condArg.Count; i++) cmd.Parameters.AddWithValue("@cond" + i, condArg[i]); using (SqlDataAdapter ada = new SqlDataAdapter(cmd)) { DataSet ds = new DataSet(); if (ada.Fill(ds) > 0) return ds.Tables[0]; else return null; } } } catch { return null; } } // public override List<double> GetDblCol() { try { List<object> condArg; string sql = GetSelectSql(out condArg); if (sql.Length == 0) return new List<double>(); // using (SqlConnection cnn = new SqlConnection(myCnnStr)) { cnn.Open(); SqlCommand cmd = cnn.CreateCommand(); cmd.CommandText = sql; for (int i = 0; i < condArg.Count; i++) cmd.Parameters.AddWithValue("@cond" + i, condArg[i]); using (SqlDataReader dr = cmd.ExecuteReaderAsync().Result) { List<double> lst = new List<double>(); while (dr.Read()) lst.Add(dr.GetDouble(0)); return lst; } } } catch { return new List<double>(); } } // public override List<string> GetStrCol() { try { List<object> condArg; string sql = GetSelectSql(out condArg); if (sql.Length == 0) return new List<string>(); // using (SqlConnection cnn = new SqlConnection(myCnnStr)) { cnn.Open(); SqlCommand cmd = cnn.CreateCommand(); cmd.CommandText = sql; for (int i = 0; i < condArg.Count; i++) cmd.Parameters.AddWithValue("@cond" + i, condArg[i]); using (SqlDataReader dr = cmd.ExecuteReaderAsync().Result) { List<string> lst = new List<string>(); while (dr.Read()) lst.Add(dr.GetString(0)); return lst; } } } catch { return new List<string>(); } } // } }
tSqlQuery类的实现与tMySqlQuery类的区别主要体现在以下几点: