C#开发训练营

第15课:数据库操作基础——tJet接口组件

tJet接口及相关组件的功能是对数据库进行直接操作,并提供一些辅助成员以帮助实现更多的操作组件。

tJet接口

首先来看tJet接口的实现,如下面的代码(/app_code/data/base/tJet.cs)。

C#
public interface tJet
{
    string CnnStr { get; }
    bool Connect();
    //
    string GetObjName(string name);
    string GetParamName(string name);
    char MatchChar { get; }
    string Database { get; }
    //
    bool Execute(string sql, params tPair[] args);
    int ExecuteNonQuery(string sql, params tPair[] args);
    tPair GetValue(string sql, params tPair[] args);
    tPairList GetFirstRow(string sql, params tPair[] args);
    List<object> GetFirstColumn(string sql, params tPair[] args);
    DataTable GetTable(string sql, params tPair[] args);
    //
    List<double> GetData(string sql, params tPair[] args);
}

tJet接口中首先定义了几个基本的成员,如:

  • CnnStr只读属性,返回数据库连接字符串,此属性需要使用构造函数指定。
  • Connect()方法,测试数据库连接,成功时返回true,否则返回false。
  • GetObjName(string name)方法,返回数据库中的对象名称格式,如username字段在SQL Server数据库中的格式为[username],在MySQL数据库中的格式为`username`(使用两个重音符号)。
  • GetParamName(string name)方法,返回参数名称格式,如arg_0在SQL Server数据库中的格式为@arg_0,在MySQL数据库中的格式为?arg_0。
  • MatchChar只读属性,返回数据库中模糊查询(like)时的任意个字符匹配符,如%。
  • Database只读属性,返回连接的数据库名称。

接下来是一系列SQL语句执行方法,并按指定格式返回数据。这些方法是:

  • Execute()方法,执行SQL,成功是返回true,失败时返回false。
  • ExecuteNonQuery()方法,执行SQL,返回影响的记录数量。
  • GetValue()方法,返回查询结果中第一条记录第一个字段的数据,返回类型为tPair对象;获取数据后,可以使用tPair对象中的一系列GetXXX()方法返回所需要的格式。
  • GetFirstRow()方法,返回查询结果中第一条记录的数据,返回类型类型为tPairList对象。
  • GetFirstColumn()方法,返回查询结果中所有记录中的第一个字段数据,返回类型为List<object>对象。
  • GetTable()方法,返回所有查询结果,返回类型为DataTable对象。
  • GetData()方法,以double类型返回查询结果中所有记录中的第一个字段数据,返回类型为List<double>对象。

tJet接口中的一系列SQL语句执行方法中使用了相同的参数设置,其中,参数一指定SQL语句,参数二设置为参数数组,使用tPair对象分别指定SQL语句中需要的参数,包括参数名称为和参数数据。

tJetBase基类

实现tJet接口的基本类型使用tJetBase类,如下面的代码(/app_code/data/base/tJet.cs)。

C#
public abstract class tJetBase : tJet
{
    public tJetBase(string sCnnStr)
    {
        CnnStr = sCnnStr;
    }
    //
    public string CnnStr { get; private set; }
    public abstract bool Connect();
    //
    public abstract string GetObjName(string name);
    public abstract string GetParamName(string name);
    public abstract char MatchChar { get; }
    public abstract string Database { get; }
    //
    public abstract bool Execute(string sql, params tPair[] args);
    public abstract int ExecuteNonQuery(string sql, params tPair[] args);
    public abstract tPair GetValue(string sql, params tPair[] args);
    public abstract tPairList GetFirstRow(string sql, params tPair[] args);
    public abstract List<object> GetFirstColumn(string sql, params tPair[] args);
    public abstract DataTable GetTable(string sql, params tPair[] args);
    //
    public abstract List<double> GetData(string sql, params tPair[] args);
}

tJetBase类中,首先定义了构造函数,其参数为数据库连接字符串;CnnStr属性定义为公共属性,但set部分需要使用private关键字,以保证其设置为只读属性。tJet接口中的其它成员都定义为抽象的(abstract)。

tSqlJet类

接下来是tJet接口在SQL Server数据库的应用,实现类型为tSqlJet,代码位于/app_code/data/sqlserver/tSqlJet.cs文件。tSqlJet类的基本定义如下。

C#
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;

public class tSqlJet : tJetBase
{
    public tSqlJet(string sCnnStr):base(sCnnStr)
    {    }
    // 其它代码…
}

这里,tSqlJet类定义为tJetBase类的子类,构造函数同样需要指定数据库连接字符串,并通过继承基类的构造函数实现。

接下来是几个基本成员的实现,如下面的代码。

C#
//
public override bool Connect()
{
    try
    {
        using(SqlConnection cnn = new SqlConnection(CnnStr))
        {
            cnn.Open();
            return true;
        }
    }
    catch (Exception ex)
    {
        tLog.E(ex, -1000, "tSqlJet.Connect()");
        return false;
    }
}
//
public override string GetObjName(string name)
{
    return "["+name+"]";
}
//
public override string GetParamName(string name)
{
    return "@" + name;
}
//
public override char MatchChar { get { return '%'; } }
//
public override string Database
{
    get
    {
        SqlConnectionStringBuilder sb = 
            new SqlConnectionStringBuilder(CnnStr);
        return sb.InitialCatalog;
    }
}

代码中实现的成员包括:

  • Connect()方法中,通过SqlConnection对象进行数据库连接测试,如果能正确连接数据库则返回true,否则返回false。
  • GetObjName()方法会在参数的前后添加中括号,以返回SQL Server数据库中的对象名称格式。
  • GetParamName()方法会在参数名称前添加@符号。
  • MatchChar只读属性会直接返回%符号。
  • Database只读属性中,会通过数据库连接字符串(CnnStr属性)生成SqlConnectionStringBuilder对象,然后通过其中的InitialCatalog属性返回连接的数据库名称。

在tJet接口组件的一系列SQL语句执行方法中都可以使用参数,此时,就需要向SqlCommand对象添加这些参数数据,此功能是AddParams()方法实现,如下面的代码。

C#
protected void AddParams(SqlCommand cmd,params tPair[] args)
{
    if(args.Length > 0)
    {
        for (int i = 0; i < args.Length; i++)
            cmd.Parameters.AddWithValue(
                GetParamName(args[i].Name), 
                args[i].Value);
    }
}

接下来是SQL语句执行方法,首先看Execute()方法的实现,如下面的代码。

C#
public override bool Execute(string sql, params tPair[] args)
{
    try
    {
        using(SqlConnection cnn = new SqlConnection(CnnStr))
        {
            cnn.Open();
            SqlCommand cmd = cnn.CreateCommand();
            cmd.CommandText = sql;
            AddParams(cmd,args);
            IAsyncResult ar = cmd.BeginExecuteNonQuery();
            cmd.EndExecuteNonQuery(ar);
            return true;
        }
    }
    catch(Exception ex)
    {
        tLog.E(ex, -1000, "tSqlJet.Execute()");
        return false;
    }
}

tSqlJet.ExecuteNonQuery()方法的实现与Execute()方法比较相似,只是方法会直接返回EndExecuteNonQuery()方法的返回值,如下面的代码。

C#
public override int ExecuteNonQuery(string sql, params tPair[] args)
{
    try
    {
        using (SqlConnection cnn = new SqlConnection(CnnStr))
        {
            cnn.Open();
            SqlCommand cmd = cnn.CreateCommand();
            cmd.CommandText = sql;
            AddParams(cmd, args);
            IAsyncResult ar = cmd.BeginExecuteNonQuery();
            return cmd.EndExecuteNonQuery(ar);
        }
    }
    catch (Exception ex)
    {
        tLog.E(ex, -1000, "tSqlJet.ExecuteNonQuery()");
        return -1000;
    }
}

接下来是读取SQL语句查询结果的方法,首先是GetValue()方法的实现,如下面的代码。

C#
public override tPair GetValue(string sql, params tPair[] args)
{
    try
    {
        using (SqlConnection cnn = new SqlConnection(CnnStr))
        {
            cnn.Open();
            SqlCommand cmd = cnn.CreateCommand();
            cmd.CommandText = sql;
            AddParams(cmd, args);
            return tPair.Get("", cmd.ExecuteScalar());
        }
    }
    catch (Exception ex)
    {
        tLog.E(ex, -1000, "tSqlJet.GetValue()");
        return tPair.Get();
    }
}

需要注意是,方法中直接使用SqlCommand对象的ExecuteScalar()方法返回标量数据,返回的tPair对象中并不包含数据名称。

返回查询结果中的第一条语句时使用GetFirstRow()方法,实现如下。

C#
public override tPairList GetFirstRow(string sql, params tPair[] args)
{
    try
    {
        using (SqlConnection cnn = new SqlConnection(CnnStr))
        {
            cnn.Open();
            SqlCommand cmd = cnn.CreateCommand();
            cmd.CommandText = sql;
            AddParams(cmd, args);
            IAsyncResult ar = cmd.BeginExecuteReader();
            using(SqlDataReader dr = cmd.EndExecuteReader(ar))
            {
                tPairList lst  =new tPairList();
                if (dr.Read())
                {
                    for (int col = 0; col < dr.FieldCount; col++)
                        lst.Add(dr.GetName(col), dr[col]);
                }
                return lst;
            }
        }
    }
    catch (Exception ex)
    {
        tLog.E(ex, -1000, "tSqlJet.GetFirstRow()");
        return tPairList.Get();
    }
}

tSqlJet.GetFirstRow()方法中会读取查询结果中的第一条记录,并以tPairList对象返回,其中包含了字段名和相应的数据;需要注意的是,当查询结果没有数据或方法执行异常时会返回一个不包含任何数据的tPairList对象,即Count属性为0的tPairList对象,而不是null。

需要查询结果所有记录中第一个字段的值时使用GetFirstColumn()方法,实现代码如下。

C#
public override List<object> GetFirstColumn(string sql, params tPair[] args)
{
    try
    {
        using (SqlConnection cnn = new SqlConnection(CnnStr))
        {
            cnn.Open();
            SqlCommand cmd = cnn.CreateCommand();
            cmd.CommandText = sql;
            AddParams(cmd, args);
            IAsyncResult ar = cmd.BeginExecuteReader();
            using (SqlDataReader dr = cmd.EndExecuteReader(ar))
            {
                List<object> lst = new List<object>();
                while (dr.Read()) lst.Add(dr[0]);
                return lst;
            }
        }
    }
    catch (Exception ex)
    {
        tLog.E(ex, -1000, "tSqlJet.GetFirstRow()");
        return new List<object>();
    }
}

tSqlJet.GetFirstColumn()方法返回类型为List<object>,如果查询结果中没有数据,同样返回一个Count属性为0的List<object>对象,而不是null。为便于对查询结果数据进行计算,可以直接从查询结果中读取double类型的数据,如下面的代码实现了tSqlJet.GetData()方法。

C#
public override List<double> GetData(string sql, params tPair[] args)
{
    try
    {
        using (SqlConnection cnn = new SqlConnection(CnnStr))
        {
            cnn.Open();
            SqlCommand cmd = cnn.CreateCommand();
            cmd.CommandText = sql;
            AddParams(cmd, args);
            IAsyncResult ar = cmd.BeginExecuteReader();
            using (SqlDataReader dr = cmd.EndExecuteReader(ar))
            {
                List<double> lst = new List<double>();
                while (dr.Read()) lst.Add(dr.GetDouble(0));
                return lst;
            }
        }
    }
    catch (Exception ex)
    {
        tLog.E(ex, -1000, "tSqlJet.GetData()");
        return new List<double>();
    }
}

tSqlJet.GetData()方法同样返回查询结果中所有记录的第一个字段的数据,返回类型为List<double>类型,对于返回的数据,还可以很方便地转换为double数组。对于获取的数据,可以通过.NET Framework类库或自定义的代码进行数据计算。

tSqlJet类中最后一个需要实现的方法是GetTable()方法,如下面的代码。

C#
public override DataTable GetTable(string sql, params tPair[] args)
{
    try
    {
        using (SqlConnection cnn = new SqlConnection(CnnStr))
        {
            cnn.Open();
            SqlCommand cmd = cnn.CreateCommand();
            cmd.CommandText = sql;
            AddParams(cmd, args);
            using (SqlDataAdapter ada = new SqlDataAdapter(cmd))
            {
                DataSet ds = new DataSet();
                ada.Fill(ds);
                if (ds.Tables[0].Rows.Count > 0) return ds.Tables[0];
                else return null;
            }
        }
    }
    catch (Exception ex)
    {
        tLog.E(ex, -1000, "tSqlJet.GetTable()");
        return null;
    }
}

tSqlJet.GetTable()方法会返回查询结果中的所有记录,返回类型为DataTable对象,当查询结果中没有数据时会返回null。

tJet接口组件是数据库操作的基础组件,可以直接用于SQL语句的执行并返回查询结果,也可以作为其它组件的支持组件,在后续课程中可以看到具体的应用。