My problem: I have .NET 2.0 application that is installed in many places. The
application uses SQL Server and of course Microsoft SQL Provider. I want to
support Firebird Database in my application. But, because you choose not to use
provided DbDataAdapter.Update implementation for FbDataAdapter, FbDataAdapter
does not behave the same way Microsoft does.
I understand the case when it is not possible to provide the same
functionality, but this is not the case.
What I do not understand is why you don't provide the same functionality if you
can, why don't you use Microsoft default implementation (I ask this but I get
an answer that did not convince me at all).
And in fact that is not my problem. This problem is everywhere. It's where you
have identity columns, where you have generators. It's described and supported
by Microsoft. FbProvider is a piece of code that is to be integrated in their
ADO.NET environment, isn't it ?
I have send Carlos two similar VS 2005 projects that contains the TestCase that
demonstrates the differences between SQLProvider and FBProvider for this
scenario.
I have send you a link that describe the problem and the scenario
http://msdn2.microsoft.com/en-us/library/ks9f57t0(VS.80).aspx
http://72.14.221.104/search?q=cache:Op4FFrqXbIkJ:msdn2.microsoft.com/en-us/ks9f57t0(VS.80).aspx+ADO.NET+performs+a+series+of+actions+to+set+the+current+values+of+the+DataRow&hl=en&ct=clnk&cd=1
and where Microsoft says: "First, the AcceptChanges
method of the DataRow is called to preserve the current values as original
values, and then the new values are assigned."
This is the code that is missing in present implementation of
FbDataAdapter.Update method:
if (statementType == StatementType.Insert)
{
row.AcceptChanges();
}
This code, as the Microsoft says, fills DataRowVersion.Original values that is
needed when performing a Merge operation between the DataSet as it was before
Updating and the DataSet that contains updated changes in the original one.
Because the link seems to be invalid i have retrieved it from google cache and
copy the whole problem here:
Merging New Identity Values
A common scenario is to call the GetChanges method of a DataTable to create a
copy that contains only changed rows, and to use the new copy when calling the
Update method of a DataAdapter. This is especially useful when you need to
marshal the changed rows to a separate component that performs the update.
Following the update, the copy can contain new identity values that must then
be merged back into the original DataTable. The new identity values are likely
to be different from the original values in the DataTable. To accomplish the
merge, the original values of the AutoIncrement columns in the copy must be
preserved, in order to be able to locate and update existing rows in the
original DataTable, rather than appending new rows containing the new identity
values. However, by default those original values are lost after a call to the
Update method of a DataAdapter, because AcceptChanges is implicitly called for
each updated DataRow.
There are two ways to preserve the original values of a DataColumn in a DataRow
during a DataAdapter update:
The first method of preserving the original values is to set the
AcceptChangesDuringUpdate property of the DataAdapter to false. This affects
every DataRow in the DataTable being updated. For more information and a code
example, see AcceptChangesDuringUpdate.
The second method is to write code in the RowUpdated event handler of the
DataAdapter to set the Status to SkipCurrentRow. The DataRow is updated but the
original value of each DataColumn is preserved. This method enables you to
preserve the original values for some rows and not for others. For example,
your code can preserve the original values for added rows and not for edited or
deleted rows by first checking the StatementType and then setting Status to
SkipCurrentRow only for rows with a StatementType of Insert.
When either of these methods is used to preserve original values in a DataRow
during a DataAdapter update, ADO.NET performs a series of actions to set the
current values of the DataRow to new values returned by output parameters or by
the first returned row of a result set, while still preserving the original
value in each DataColumn. First, the AcceptChanges method of the DataRow is
called to preserve the current values as original values, and then the new
values are assigned. Following these actions, DataRows that had their RowState
property set to Added will have their RowState property set to Modified, which
may be unexpected.
How the command results are applied to each DataRow being updated is determined
by the UpdatedRowSource property of each DbCommand. This property is set to a
value from the System.Data.UpdateRowSource enumeration.
The following table describes how the UpdateRowSource enumeration values affect
the RowState property of updated rows.
Member name Description
Both AcceptChanges is called and both output parameter values and/or the values
in the first row of any returned result set are placed in the DataRow being
updated. If there are no values to apply, the RowState will be Unchanged.
FirstReturnedRecord If a row was returned, AcceptChanges is called and the row
is mapped to the changed row in the DataTable, setting the RowState to
Modified. If no row is returned, then AcceptChanges is not called and the
RowState remains Added.
None Any returned parameters or rows are ignored. There is no call to
AcceptChanges and the RowState remains Added.
OutputParameters AcceptChanges is called and any output parameters are mapped
to the changed row in the DataTable, setting the RowState to Modified. If there
are no output parameters, the RowState will be Unchanged.
Example
This example demonstrates extracting changed rows from a DataTable and using a
SqlDataAdapter to update the data source and retrieve a new identity column
value. The InsertCommand executes two Transact-SQL statements; the first one is
the INSERT statement, and the second one is a SELECT statement that uses the
SCOPE_IDENTITY function to retrieve the identity value.
INSERT INTO dbo.Shippers (CompanyName)
VALUES (@CompanyName);
SELECT ShipperID, CompanyName FROM dbo.Shippers
WHERE ShipperID = SCOPE_IDENTITY();
The UpdatedRowSource property of the insert command is set to
UpdateRowSource.FirstReturnedRow and the MissingSchemaAction property of the
DataAdapter is set to MissingSchemaAction.AddWithKey. The DataTable is filled
and the code adds a new row to the DataTable. The changed rows are then
extracted into a new DataTable, which is passed to the DataAdapter, which then
updates the server.
Visual Basic
Private Sub MergeIdentityColumns(ByVal connectionString As String)
Using connection As SqlConnection = New SqlConnection( _
connectionString)
' Create the DataAdapter
Dim adapter As SqlDataAdapter = New SqlDataAdapter( _
"SELECT ShipperID, CompanyName FROM dbo.Shippers", connection)
' Add the InsertCommand to retrieve new identity value.
adapter.InsertCommand = New SqlCommand( _
"INSERT INTO dbo.Shippers (CompanyName) " & _
"VALUES (@CompanyName); " & _
"SELECT ShipperID, CompanyName FROM dbo.Shippers " & _
"WHERE ShipperID = SCOPE_IDENTITY();", _
connection)
' Add the parameter for the inserted value.
adapter.InsertCommand.Parameters.Add( _
New SqlParameter("@CompanyName", SqlDbType.NVarChar, 40, _
"CompanyName"))
adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.Both
' MissingSchemaAction adds any missing schema to
' the DataTable, including identity columns
adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey
' Fill the DataTable.
Dim shipper As New DataTable
adapter.Fill(shipper)
' Add a new shipper.
Dim newRow As DataRow = shipper.NewRow()
newRow("CompanyName") = "New Shipper"
shipper.Rows.Add(newRow)
' Add changed rows to a new DataTable. This
' DataTable will be used by the DataAdapter.
Dim dataChanges As DataTable = shipper.GetChanges()
' Add the event handler.
AddHandler adapter.RowUpdated, New _
SqlRowUpdatedEventHandler(AddressOf OnRowUpdated)
' Update the datasource with the modified records.
adapter.Update(dataChanges)
' Merge the two DataTables.
shipper.Merge(dataChanges)
' Commit the changes.
shipper.AcceptChanges()
Console.WriteLine("Rows after merge.")
Dim row As DataRow
For Each row In shipper.Rows
Console.WriteLine("{0}: {1}", row(0), row(1))
Next
End Using
End Sub
C#
private static void MergeIdentityColumns(string connectionString)
{
using (SqlConnection connection =
new SqlConnection(connectionString))
{
// Create the DataAdapter
SqlDataAdapter adapter =
new SqlDataAdapter(
"SELECT ShipperID, CompanyName FROM dbo.Shippers",
connection);
//Add the InsertCommand to retrieve new identity value.
adapter.InsertCommand = new SqlCommand(
"INSERT INTO dbo.Shippers (CompanyName) " +
"VALUES (@CompanyName); " +
"SELECT ShipperID, CompanyName FROM dbo.Shippers " +
"WHERE ShipperID = SCOPE_IDENTITY();", connection);
// Add the parameter for the inserted value.
adapter.InsertCommand.Parameters.Add(
new SqlParameter("@CompanyName", SqlDbType.NVarChar, 40,
"CompanyName"));
adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.Both;
// MissingSchemaAction adds any missing schema to
// the DataTable, including identity columns
adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
// Fill the DataTable.
DataTable shipper = new DataTable();
adapter.Fill(shipper);
// Add a new shipper.
DataRow newRow = shipper.NewRow();
newRow["CompanyName"] = "New Shipper";
shipper.Rows.Add(newRow);
// Add changed rows to a new DataTable. This
// DataTable will be used by the DataAdapter.
DataTable dataChanges = shipper.GetChanges();
// Add the event handler.
adapter.RowUpdated +=
new SqlRowUpdatedEventHandler(OnRowUpdated);
adapter.Update(dataChanges);
connection.Close();
// Merge the updates.
shipper.Merge(dataChanges);
// Commit the changes.
shipper.AcceptChanges();
Console.WriteLine("Rows after merge.");
foreach (DataRow row in shipper.Rows)
{
{
Console.WriteLine("{0}: {1}", row[0], row[1]);
}
}
}
}
The OnRowUpdated event handler checks the StatementType of the
SqlRowUpdatedEventArgs to determine if the row is an insert. If it is, then the
Status property is set to SkipCurrentRow. The row is updated, but the original
values in the row are preserved. In the main body of the procedure, the Merge
method is called to merge the new identity value into the original DataTable,
and finally AcceptChanges is called.
Visual Basic
Private Sub OnRowUpdated( _
ByVal sender As Object, ByVal e As SqlRowUpdatedEventArgs)
' If this is an insert, then skip this row.
If e.StatementType = StatementType.Insert Then
e.Status = UpdateStatus.SkipCurrentRow
End If
End Sub
C#
protected static void OnRowUpdated(
object sender, SqlRowUpdatedEventArgs e)
{
// If this is an insert, then skip this row.
if (e.StatementType == StatementType.Insert)
{
e.Status = UpdateStatus.SkipCurrentRow;
}
}
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Firebird-net-provider mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/firebird-net-provider