C#实现数据查询组件

本文会实现数据查询组件,示例主要使用MySQL数据库,相关准备知识可以参考如下资源:

《MySQL数据库应用》合集,http://caohuayu.com/article/Group.aspx?id=3

《数据查询》文章,http://caohuayu.com/article/Article.aspx?id=a261005

tQuery接口

tQuery接口定义了数据查询的组件标准,代码如下(cfx/data/tQuery.cs)。

C#
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两个只读属性,分别用于返回数据库连接字符串和查询数据源名称(如表、视图等)。

接下来是设置查询要素的方法,包括:

  • SetCond()方法,两个重载版本,分别使用tCond对象和字符串设置查询条件。
  • SetLimit()方法,设置查询结果返回的最大记录数量。
  • SetField()方法,通过字符串设置返回的字段,多个字段使用逗号分隔。
  • AddOrderBy()方法,添加排序字段和排序方法。参数一指定排序字段,参数二指定是否升序排列,默认为true。

执行查询并返回查询结果的方法有五个,分别是:

  • GetValue()方法,返回查询结果中第一行第一列的数据,返回类型为tPair对象;没有查询结果时返回默认值的tPair对象。
  • GetRow()方法,返回查询结果第一行,返回类型为tPairList,元素中,Name属性保存字段名,Value属性保存数据;没有查询结果时返回0个元素的tPairList对象。
  • GetTable()方法,返回查询结果数据填充的DataTable对象。没有查询结果时返回null。
  • GetDblCol()方法,返回查询结果第一列数据组成的List<double>对象。
  • GetStrCol()方法,返回查询结果第一列数据组成的List<string>对象。

tQueryBase基类

tQueryBase是实现tQuery接口组件的基类,定义如下(cfx/data/tQuery.cs):

C#
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类中,首先定义了几个字段,用于保存内部数据,分别是:

  • myCnnStr,string类型,保存数据库连接字符串。
  • mySource,string类型,保存查询数据源,如表名、视图名等。
  • myCond,tCond类型,保存查询条件。
  • myLimit,long类型,保存查询结果返回的记录数量。默认为-1,实际应用中,只有大于0才添加到查询语句中。
  • myField,定义为List<string>对象,保存查询结果返回的字段。
  • myOrderBy,定义为tPairList对象,保存排序字段和排序方法。

接下来,构造函数的参数需要指定数据库连接字符串和查询数据源。一系列的Set方法会将设置的查询要素保存到对应的字段中;AddOrderBy()方法可以多次调用添加多个排序字段和排序方法,并保存到myOrderBy对象,元素(tPair类型)的Name属性保存排序字段名,Value属性保存排序方法关键字,如asc(升序,默认值)和desc(降序)。

执行查询的方法则定义为抽象类,需要在子类中实现。

MySQL数据查询——tMySqlQuery类

下面的代码(cfx/data/mysql/tMySqlQuery.cs)是tMySqlQuery类的实现,用于MySQL数据库的数据查询操作。

C#
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类

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、DataSet类

示例中,MySqlDataAdapter类使用MySqlCommand对象作为构造函数的参数,并使用Fill()方法将查询结果填充到DataSet对象;Fill()方法会返回填充的记录数量,示例中,当查询结果包含数据时则返回DataSet对象中的第一个表(DataTable类型),使用Tables[0]获取;这里,DataSet对象的Tables属性包含了对象中的数据表集合。

准备测试数据

在HeidiSQL中执行如下代码,以创建MySQL数据库的查询测试数据。

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条记录。

测试tMySqlQuery类

下面的代码,我们在Program.cs文件中测试tMySqlQuery类的应用。

C#
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。

下面的代码会读取一行数据。

C#
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的记录,并分行显示。

C#
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列的所有数据。

C#
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字段降序显示数据。

C#
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]);
            }
        }
    }
}

执行结果如下图所示。

降序排列

SQL Server数据查询——tSqlQuery类

下面的代码(cfx/data/sql/tSqlQuery.cs),使用tSqlQuery类实现SQL Server数据库数据查询操作。

C#
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类的区别主要体现在以下几点:

  • tSqlQuery类使用cfx.data.sql和System.Data.SqlClient命名空间下的资源。如tSqlCondBuilder、SqlConnection、SqlCommand、SqlDataReader、SqlAdapter等。
  • GetSelectSql()方法中,指定查询结果返回记录数量时使用top子句,并且定义在select关键字后,返回字段之前;而tMySqlQuery类中,使用limit子句,定义在select语句的最后。
  • SQL Server数据库对象名称使用一对方括号定义,而MySQL数据库对象名称使用一对反单引号(`)定义。
  • 约定SQL Server查询条件中的参数使用@符号定义,而MySQL数据库中使用?定义。