65.9K
CodeProject 正在变化。 阅读更多。
Home

如何在 ADO.NET 应用程序中刷新当前行

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.11/5 (3投票s)

2007年2月27日

4分钟阅读

viewsIcon

55160

downloadIcon

645

使用 ODP.NET 数据提供程序在 ADO.NET 应用程序中刷新当前行

Screenshot - currowrefresh.gif

引言

如今,大多数应用程序都使用不同的数据库。对于 .NET 程序,访问数据库最流行的方式是使用 ADO.NET 组件。众所周知,ADO.NET 使用断开连接模型和名为 DataTable 的组件,该组件没有“当前数据行”的概念。但开发人员经常需要刷新当前数据行,因为该行可能已被另一个应用程序或服务器端逻辑更改。我认为下面的代码是 Oracle 数据库对此问题的解决方案,但同样适用于任何其他 RDBMS。

必备组件

所有代码示例都适用于 Microsoft Visual Studio 2005。我使用的数据库是 Oracle 10i 和 Oracle Data Provider (ODP.NET) 版本 10.2.0.2.20。您可以在 Oracle 网站上下载:http://www.oracle.com/technology/software/tech/windows/odpnet/index.html

现实

ADO.NET 开发人员给了我们一个非常“有用”的事件,名为 RowUpdated。如果您使用 DataAdapter 组件更新了一行,您就会知道您已经更新了。这很好,但如果该行已被另一个用户或服务器端逻辑更新,您将一无所知。在我之前的文章中,我考虑了 ADO.NET 并发模型的一些方面。原则上,本文是前一篇文章的逻辑延续,因为两篇文章有一个共同的目标:解决 ADO.NET 模型限制。

解决问题的方法

我们应该解决以下问题:

  1. DataTable 获取当前行
  2. 从 Oracle 数据库获取与当前行对应的记录
  3. 将数据从记录放入 DataTable(刷新当前行)

众所周知,ADO.NET 的 dataset 没有游标的概念,因此它也没有“当前行”的概念。但是,如果我们使用的应用程序有一个在窗体上的 DataGridView 组件,这个问题就很容易解决。我们可以简单地使用名为“BindingContext”的属性,并从 DataGridView 获取当前行位置。这只需要三行代码。

public partial class Form1 : Form
{
        ..........
        private BindingManagerBase bindingManagerBase;
        public Form1()
        {
              InitializeComponent();
              ..........              
  bindingManagerBase = 
       dataGridView1.BindingContext[dataSet1.Tables["Colors"]];
  ..........

在应用程序代码的某个地方,我们可以获取当前行:

DataRow cur_row = dataSet1.Tables["Colors"].DefaultView
            [bindingManagerBase.Position].Row;

所以,“1”点很简单。

要解决“2”和“3”点,我们首先需要决定如何标识当前行以便从数据库中获取它。显然,我们应该使用某个键。大多数数据库表都有主键,因此我们可以使用它们来标识我们的行。但为了使我们的解决方案更通用,我们可以使用字段名“ROWID”。 Oracle 数据库的每个表和视图都有这个字段,但如果您需要在另一个 RDBMS 中刷新当前行,您应该使用其他内容。既然我们决定使用 ROWID 作为标识行的键,请不要忘记在您的“SELECT”语句中包含 ROWID。

然后,我们可以编写一个名为“RefreshRow”的类,它具有以下公共方法和属性:

public  RefreshRow(OracleConnection conn) //constructor
public void Dispose() //destructor
public void Refresh(DataRow row) //refreshing row

public String SQL 
// sql expression to extract row from database, 
//it should has rowid parameter obligatory, 
//for example "select t.*, 
//t.rowid from colors t where t.rowid=:snotra__rowid"

Refresh 方法的工作方式非常简单:它从数据库提取一行并将数据放入 DataRow

public void Refresh(DataRow row)
{
    OpenDMLQuery();
    oracleDMLCommand.CommandText = sql;
    oracleDMLCommand.Parameters.Add(":snotra__rowid", OracleDbType.Varchar2,   
                                  RowId(row), ParameterDirection.Input);
    OracleDataReader odr = null;
    try
    {
          odr = oracleDMLCommand.ExecuteReader();
          if (odr.Read())
          {
               for (int i = 0; i < odr.FieldCount; i++)
               {
                   DataColumn col = row.Table.Columns[odr.GetName(i)];
                   SetColumnValue(col, i, odr, row);
               }
          }
    }
    catch (OracleException oe)
    {
       System.Windows.Forms.MessageBox.Show(oe.Message, "Error", 
            System.Windows.Forms.MessageBoxButtons.OK, 
        System.Windows.Forms.MessageBoxIcon.Error);
    }
    catch (Exception e)
    {
       System.Windows.Forms.MessageBox.Show(e.Message, "Error", 
                System.Windows.Forms.MessageBoxButtons.OK, 
                System.Windows.Forms.MessageBoxIcon.Error);
    }
    finally
    {
       if (odr != null)
       {
          odr.Close();
          odr.Dispose();
       }
    }
}

私有方法 SetColumnValue 根据列的数据类型将数据放入行。

private void SetColumnValue(DataColumn col, int index, OracleDataReader odr, 
    DataRow row)
{
     if (odr.GetValue(index) == DBNull.Value)
     {
                row[col] = DBNull.Value;
                return;
     }
     try
     {
          row.Table.BeginLoadData();
          switch (Type.GetTypeCode(/*col.DataType*/odr.GetFieldType(index)))
          {
                    case TypeCode.Int16:
                        row[col] = odr.GetInt16(index);
                        break;
                    case TypeCode.Int32:
                        row[col] = odr.GetInt32(index);
                        break;
                    case TypeCode.Int64:
                        row[col] = odr.GetInt64(index);
                        break;
                    case TypeCode.Decimal:
                        row[col] = odr.GetDecimal(index);
                        break;
                    case TypeCode.Single:
                        row[col] = odr.GetFloat(index);
                        break;
                    case TypeCode.Double:
                        row[col] = odr.GetDouble(index);
                        break;
                    case TypeCode.Byte:
                        row[col] = odr.GetByte(index);
                        break;
                    case TypeCode.Boolean:
                        row[col] = odr.GetBoolean(index);
                        break;
                    case TypeCode.String:
                        row[col] = odr.GetString(index);
                        break;
                    case TypeCode.DateTime:
                        row[col] = odr.GetDateTime(index);
                        break;
                    //add other types if you like
                    default:
                        row[col] = odr.GetValue(index);
                        break;
          }
     }
     catch (Exception)
     {
        throw new Exception("column: " + col.ColumnName + " cast is not 
                             valid");
     }
     finally
     {
         row.Table.EndLoadData();
     }
}

根据这一行:

"switch (Type.GetTypeCode(/*col.DataType*/odr.GetFieldType(index)))" 
此方法从 oracle DataReader 获取类型。如果您手动设置列的类型,可以取消注释“col.DataType”行。

构建应用程序

在我们的实验中,我们将使用一个简单的 .NET 应用程序,该应用程序在 Form 上包含 DataGridView 组件,DataSet 和一个名为“RefreshCurrentRow”的 Button。我们的程序应该访问 Oracle 数据库,ODP.NET 非常适合。

在我们的示例中,我们将使用一个名为“COLORS”的简单表。

create table COLORS
(
  COLOR_ID   NUMBER not null,
  COLOR_NAME varchar2(100) not null,
  RED number not null,
  GREEN number not null,
  BLUE number not null,
  CONSTRAINT PK_COLORS PRIMARY KEY (COLOR_ID)
)
--fill table-------------
insert into colors(color_id, color_name, red, green, blue) 
        values(1, 'black', 0, 0, 0);
insert into colors(color_id, color_name, red, green, blue) 
        values(2, 'white', 254, 254, 254);
insert into colors(color_id, color_name, red, green, blue) 
        values(3, 'red', 254, 0, 0);
insert into colors(color_id, color_name, red, green, blue) 
        values(4, 'green', 0, 254, 0);
insert into colors(color_id, color_name, red, green, blue) 
        values(5, 'blue', 0, 0, 254);
insert into colors(color_id, color_name, red, green, blue) 
        values(6, 'yellow', 0, 254, 254);
commit;

下面是如何使用 RefreshRow 类的示例:

private void button1_Click(object sender, EventArgs e)
{
            if (bindingManagerBase != null)
            {
                DataRow cur_row = 
                dataSet1.Tables["Colors"].DefaultView
            [bindingManagerBase.Position].Row;
                if (cur_row != null)
                {
                    RefreshRow refreshRow = new RefreshRow(connection);
                    refreshRow.SQL = "select t.*, 
                t.rowid from colors t where 
                     t.rowid=:snotra__rowid";
                    refreshRow.Refresh(cur_row);
                    refreshRow.Dispose();
                }
            }
}

它需要一个 OracleConnection 对象、一个包含 ROWID 列(必需)的 DataRow 对象和一个具有以下结构的 SQL 语句:

"t.rowid=:snotra__rowid"

就是这样。现在我们可以进行测试了。

测试

首先运行测试应用程序。然后启动 sqlplus 并执行命令:

update colors set red=1, green=1, blue=1 where color_id=1;
commit;

然后选择 DataGridView 中的第一行(颜色名称为 black)并按“RefreshCurrentRow”按钮。REDGREENBLUE 列的值应该会改变。所以……太棒了,它奏效了!

如果您更喜欢其他 RDBMS 或其他数据提供程序,您应该使用适当的数据提供程序和数据类型重写 RefreshRow 类。

关注点

我从 Delphi 转到 .NET,并且对断开连接的 ADO.NET 组件提供的可能性感到有些沮丧。结果,我决定编写自己的解决方案,该解决方案允许我使用我需要的并发模型和其他一些附加功能,例如单记录刷新、部分数据选择等。

© . All rights reserved.