Overview
Information about the changes in a dataset is maintained in two ways:
by flagging the row that indicates whether it has been changed (RowState), and by
keeping multiple copies of a record (DataRowVersion). By using this information,
processes can determine what has changed in the dataset and can send appropriate updates
to the data source.
The RowState property of a DataRow object is a value that provides
information about the status of a particular row of data.
The following table details the possible values of the DataRowState
enumeration:
|
|
Added
|
The row has been added as an item to a DataRowCollection.
(A row in this state does not have a corresponding original version since it did
not exist at the time the last AcceptChanges method was called).
|
Deleted
|
The row was deleted using the
DataRow.Delete Method of a DataRow object.
|
Detached
|
The row has been created but is not part of any
DataRowCollection. A DataRow object is in this state immediately after it has been
created and before it is added to a collection, or if it has been removed from a
collection.
|
Modified
|
A column value in the row has been changed in some way.
|
Unchanged
|
The row has not changed since AcceptChanges was last called.
|
Datasets maintain multiple versions of records. The DataRowVersion
enumeration of a DataRow object is a value that can be used to return a specific version
of a DataRow object.
The following table details the possible values of the DataRowVersion
enumeration:
|
|
Current
|
The current version of a record contains all modifications
performed on the record since the last time AcceptChanges has been called.
If the row has been deleted there is no current version.
|
Default
|
The default value of a record, as defined by the dataset schema
or data source.
|
Original
|
The original version of a record is a copy of the record as it
was the last time changes were committed in the dataset. In practical terms, this
is typically the version of a record as read from a data source.
|
Proposed
|
The proposed version of a record that is available temporarily,
while you are in the middle of an update — that is, between the time you
called the BeginEdit method and the EndEdit method. You typically
access the proposed version of a record in a handler for an event such as
RowChanging. Invoking the CancelEdit method reverses the changes and
deletes the proposed version of the data row.
|
The original and current versions are useful when update information is
transmitted to a data source. Typically, when an update is sent to the data source, the
new information for the database is in the current version of a record, and information
from the original version is used to locate the record to update. For example, in a case
where the primary key of a record is changed, you must have a way to locate the proper
record in the data source, in order to update the changes. If no original version
existed, the record would most likely be appended to the data source resulting not only
in an extra unwanted record, but in one record that is inaccurate and out of date. The
two versions are also used in concurrency control; you can compare the original version
against a record in the data source to determine if the record has changed since it was
loaded into the dataset.
The proposed version is useful when you need to perform validation
before actually committing the changes to the dataset.
Even if records have changed, there are not always original or current
versions of that row. When you insert a new row into the table, there is no original
version, only a current version. Similarly, if you delete a row by calling the table's
Delete method, there is an original version, but no current version.
You can test to see if a specific version of a record exists by
querying a data row's HasVersion property. You can access either version of a record by
passing a DataRowVersion enumeration value as an optional argument when you request the
value of a column.
Example
This ADO.NET example will demonstrate using a
DataSet and a DataAdapter to retrieve data from SQL Server, and post changes such as
inserts, updates, and deletes through the adapter. During these
changes the RowState Property will be shown.
It's worth noting here than when creating commands
to commit changes for the DataAdapter, it is important to indicate the source column. You
can do that by setting the SourceColumn property on the Parameter object, or by passing
it as the argument immediately following the parameter's data type size.
For example, the parameter, @RegionDescription, when
the DataAdapter attempted to invoke the Update command, couldn't decipher on its own that
the value to be supplied for @RegionDescription was the RegionDescription column. By
supplying the SourceColumn to the Parameters.Add() method on our own, we guarantee that
the adapter will supply the right column values for the right arguments.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Data.SqlClient;
namespace AdoNetSample
{
public class AdoNetSample : System.Windows.Forms.Form
{
public System.Windows.Forms.RichTextBox
richTextBox;
private System.ComponentModel.Container
components = null;
public string msg;
public AdoNetSample()
{
InitializeComponent();
DataSet SampleDS = new
DataSet("SampleDS");
string ConnectionString
= "Data Source=XEON; "+
"Initial Catalog=Northwind; User id=sa; Password=manager;";
SqlConnection Connection
= new SqlConnection(ConnectionString);
SqlDataAdapter SampleDA
= new SqlDataAdapter(
"SELECT RegionID,RegionDescription FROM Region", Connection);
//
Now lets provide SQL statements that support the rest of the
// UPDATE, DELETE, and
INSERT functionality.
// It's worth noting
here than when creating commands to commit
// changes for the
DataAdapter, it is important to indicate the source column.
// You can do that by
setting the SourceColumn property on the Parameter object,
// or by passing it as
the argument immediately following the parameter's data type size.
// For example, the
parameter, @RegionDescription, when the DataAdapter
// attempted to invoke
the Update command, couldn't decipher on its own that the value
// to be supplied for
@RegionDescription was the RegionDescription column.
// By supplying the
SourceColumn to the Parameters.Add() method on our own,
// we guarantee that the
adapter will supply the right column values for the right arguments.
//
-- UPDATE --
SqlCommand UpdateCommand
= new SqlCommand(
"UPDATE Region SET RegionDescription = @RegionDescription " +
"WHERE RegionID = @RegionID", Connection);
SqlParameter
UpdateKeyParam = new SqlParameter("@RegionID",SqlDbType.Int);
UpdateKeyParam.SourceColumn = "RegionID";
UpdateKeyParam.SourceVersion = DataRowVersion.Original;
UpdateCommand.Parameters.Add("@RegionDescription", SqlDbType.NChar, 50,
"RegionDescription");
UpdateCommand.Parameters.Add(UpdateKeyParam);
SampleDA.UpdateCommand =
UpdateCommand;
//
-- DELETE --
SqlCommand DeleteCommand
= new SqlCommand(
"DELETE Region WHERE RegionID = @RegionID", Connection);
SqlParameter
DeleteKeyParam = new SqlParameter("@RegionID",SqlDbType.Int);
DeleteKeyParam.SourceColumn = "RegionID";
DeleteKeyParam.SourceVersion = DataRowVersion.Original;
DeleteCommand.Parameters.Add(DeleteKeyParam);
SampleDA.DeleteCommand =
DeleteCommand;
//
-- INSERT --
SqlCommand InsertCommand
= new SqlCommand(
"INSERT INTO Region(RegionDescription, RegionID) "+
"VALUES(@RegionDescription, @RegionID)", Connection);
InsertCommand.Parameters.Add("@RegionDescription", SqlDbType.NChar,
50,"RegionDescription");
InsertCommand.Parameters.Add("@RegionID", SqlDbType.Int, 4, "RegionID");
SampleDA.InsertCommand =
InsertCommand;
//
Now load the dataset with the results of our SQL Query.
// notice that we're not
explicitly opening our conection.
// Our DataAdapter is
doing all that work for us, closing
// it as soon as it
completes its task.
SampleDA.Fill(SampleDS,
"Region");
//
Create a new row the Region table doesn't have an
// autonumbering
identity, so we have to supply our own region ID.
DataRow NewRow;
NewRow =
SampleDS.Tables["Region"].NewRow();
NewRow["RegionDescription"] = "Central";
NewRow["RegionID"] =
5;
msg = "New Row Created
using NewRow(), RowState is: " + NewRow.RowState.ToString() + "\n";
SampleDS.Tables["Region"].Rows.Add(NewRow);
msg += "New Row Added to
Table RowState is: " + NewRow.RowState.ToString() + "\n";
//
Modify the first row
DataRow TempRow;
TempRow =
SampleDS.Tables["Region"].Rows[0];
msg += "Modifying First
Row, Pre-Change State is: " + TempRow.RowState.ToString() + "\n";
TempRow["RegionDescription"] = "The wonderful Region of Berne";
msg += "Modifying First
Row, Post-Change State is: " + TempRow.RowState.ToString() + "\n";
//
Call the update method to save the new row and update the first
msg += "Calling Update()
to Commit New Row and First Row Change.\n";
SampleDA.Update(SampleDS, "Region");
//
Delete the second row
msg += "Deleting Our New
Row, Post-Delete State is: " + NewRow.RowState.ToString() + "\n";
NewRow.Delete();
msg += "Deleting Our New
Row, Post-Delete State is: " + NewRow.RowState.ToString() + "\n";
//
Now call the update method.
msg += "Calling Update()
- this will post ALL of our changes, not just the update\n";
SampleDA.Update(SampleDS, "Region");
msg += "Region Table
after Update()\n------------------------------\n";
foreach (DataRow tRow in
SampleDS.Tables["Region"].Rows)
{
msg += tRow["RegionDescription"].ToString() + "\n";
}
richTextBox.Text =
msg;
SampleDS.Dispose();
SampleDA.Dispose ();
}
[STAThread]
static void Main()
{
Application.Run(new
AdoNetSample());
}
}
}
|