银河

SKYIV STUDIO

  博客园 :: 首页 :: 博问 :: 闪存 :: :: :: 订阅 订阅 :: 管理 ::

我在2005年10月9日写的一篇文章“数据库小工具(C#)”中提到:

在编写有关数据库方面的C#程序时,经常需要知道数据库的表中各字段的以下信息:
1. 用于OracleParameter(或SqlParameter,...)中的字段和属性的数据库特定的数据类型。
2. 其对应的.NET数据类型。

当时,对各个不同的数据库是分别实现这个“数据库小工具”的。

现在,让我们将这些独立的“小工具”整合在一起吧。

从上图中可以看到 Odbc、OleDb、Oracle、SQL Server、SQL Server Mobile Edition、SQL Server Compact Edition、SQLite、MySql 等数据库都已经整合在同一个程序中了。

上图显示执行一条 SQL 语句后的结果。

上图显示出数据库中的表的结构。

好了,我们来看源程序吧:

using System;
using System.IO;
using System.Data;
using System.Data.Common;
using System.Windows.Forms;

namespace Skyiv.Ben.DbTools
{
  public partial class MainForm : Form
  {
    public MainForm()
    {
      InitializeComponent();
    }

    private void MainForm_Load(object sender, EventArgs e)
    {
      try
      {
        tbxDsn.Text = "Data Source=:memory:";
        tbxSql.Text = "select sqlite_version()";
        var table = DbProviderFactories.GetFactoryClasses();
        lbxDbProvider.ValueMember = "InvariantName";
        lbxDbProvider.DataSource = table;
        dgvMain.DataSource = table;
        lbxDbProvider.SelectedValue = "System.Data.SQLite";
      }
      catch (Exception ex)
      {
        tbxMessage.AppendText(Pub.GetMessage(ex));
      }
    }

    private void btnSubmit_Click(object sender, EventArgs e)
    {
      btnSubmit.Enabled = false;
      try
      {
        tbxMessage.Clear();
        var factory = DbProviderFactories.GetFactory(lbxDbProvider.SelectedValue.ToString());
        using (var conn = factory.CreateConnection())
        {
          conn.ConnectionString = tbxDsn.Text;
          conn.Open();
          tbxMessage.AppendLine("Client Version: [{0}]", Pub.GetClientVersion(Path.GetFileNameWithoutExtension(factory.ToString())));
          tbxMessage.AppendLine("Server Version: [{0}]", conn.ServerVersion);
          var sql = tbxSql.Text.Trim();
          if (sql.Length != 0)
          {
            var comm = conn.CreateCommand();
            comm.CommandText = sql;
            var isQuery = IsQuery(sql);
            var rows = int.MinValue;
            if (!isQuery) rows = comm.ExecuteNonQuery();
            else if (chkStruct.Checked) dgvMain.DataSource = RunQueryTableStruct(comm);
            else dgvMain.DataSource = RunQueryTableData(factory, comm);
            tbxMessage.AppendText("运行 SQL 语句完毕(" + (!isQuery ? "非查询" : (chkStruct.Checked ? "表结构" : "查询")) + ")");
            if (rows >= 0) tbxMessage.AppendText(",受影响的行数: " + rows.ToString("N0"));
          }
          else tbxMessage.AppendText("完成");
        }
      }
      catch (Exception ex)
      {
        tbxMessage.AppendText(Pub.GetMessage(ex));
      }
      btnSubmit.Enabled = true;
    }

    DataView RunQueryTableStruct(DbCommand comm)
    {
      using (var r = comm.ExecuteReader(CommandBehavior.KeyInfo))
      {
        return r.GetSchemaTable().DefaultView;
      }
    }

    DataView RunQueryTableData(DbProviderFactory factory, DbCommand comm)
    {
      var da = factory.CreateDataAdapter();
      da.SelectCommand = comm;
      var ds = new DataSet();
      da.Fill(ds);
      return ds.Tables[0].DefaultView;
    }

    bool IsQuery(string sql)
    {
      return sql.ToUpper().StartsWith("SELECT");
    }
  }
}

这个程序主要使用 System.Data.Common 命名空间中的类来进行工作 。

在 MainForm_Load 方法(第16到32行)中使用 DbProviderFactories 的静态方法 GetFactoryClasses 获得在本机中实现 DbProviderFactories 的所有已安装提供程序的信息(第22行),然后再将其绑定到程序主界面的 ListBox (第24行) 和 DataGridView (第25行) 控件上。这样,这两个控件就会联动了。

点击“执行”按钮,就会调用 btnSubmit_Click 方法(第34到68行)。在该方法中:

  • 使用 DbProviderFactories 类的静态方法 GetFactory 获得一个 DbProviderFactory (第40行)。
  • 使用 DbProviderFactory 类的 CreateConnection 方法创建一个 DbConnection (第41行)。
  • 使用 DbConnection 类的 Open 方法打开数据库连接(第44行)。
  • 使用 DbConnection 类的 CreateCommand 方法创建一个 DbCommand (第50行)。
  • 如果要执行的 SQL 语句不是 SELECT 语句,则调用 DbCommand 类的 ExecuteNonQuery 方法执行该 SQL 语句(第54行)。
  • 否则,如果程序主界面中的“结构”复选框被选中,就调用 RunQueryTableStruct 方法获得数据库中的表的结构(第55行)。
  • 否则,就调用 RunQueryTableData 方法获得数据库中的表的数据(第56行)。

第70到76行的 RunQueryTableStruct 方法使用 DbCommand 类的 ExecuteReader 方法(使用 CommandBehavior.KeyInfo 参数)获得一个 DbDataReader (第72行),然后调用 DbDataReader 类的 GetSchemaTable 方法来获得数据库中的表的各列的元数据(第74行)。

第78到85行的 RunQueryTableData 方法使用 DbProviderFactory 类的 CreateDataAdapter 方法创建一个 DbDataAdapter (第80行),然后使用 DbDataAdapter 类的 Fill 方法来填充 DataSet (第83行),最后返回该 DataSet 的 Tables 属性中的第一个 DataTabe 的 DefaultView (第84行)。

该程序中的静态类 Pub 类提供一些辅助的静态方法:

using System;
using System.Text;
using System.Windows.Forms;
using System.Reflection;

namespace Skyiv.Ben.DbTools
{
  static class Pub
  {
    public static string GetMessage(Exception ex)
    {
      var sb = new StringBuilder();
      for (sb.Append("错误: "); ex != null; ex = ex.InnerException)
      {
        sb.AppendFormat("[{0}]: ", ex.GetType());
        sb.AppendLine(ex.Message);
      }
      return sb.ToString();
    }

    public static void AppendLine(this TextBoxBase tbx, string fmt, params object[] args)
    {
      tbx.AppendText(string.Format(fmt, args) + Environment.NewLine);
    }

    public static Version GetClientVersion(string name)
    {
      foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) if (name == a.GetName().Name) return a.GetName().Version;
      return null;
    }
  }
}

最后,完整的源程序可以到 http://bitbucket.org/ben.skyiv/dbtools/downloads/ 页面下载。

也可以使用 hg clone http://bitbucket.org/ben.skyiv/dbtools/ 命令下载。

关于 hg ,请参阅 Mercurial 备忘录

posted on 2009-12-05 21:04  银河  阅读(3368)  评论(12编辑  收藏  举报