SqlDataAdapter.Fill(DataGridView.DataSource) dupli

2020-05-06 12:12发布

问题:

Simple question:

When I call SqlDataAdapter.Fill(DataGridView.DataSource) the second time after initially creating first Data it does not update the contained rows. It simply adds all rows returned by the select command to my DataGridView.

If I call it a third, fourth (so on) it will also just add the returned rows.

Am I understanding the .Fill(DataTable) function wrong? How do I update the already existing DataTable correctly? Which line of code is responsible for that?


Turns out it has to be a code problem;

    DataGridView1.AutoGenerateColumns = False
    Dim sql = "select * from myTable"
    oDtSource = New DataTable
    oAdapter = New SqlDataAdapter
    oCon = sqlCon("serverName\Instance", "myDataBase") ' Returns a SqlConnection
    oCmd = New SqlCommand(sql, oCon)
    oCon.Open()

    oDtSource.Clear()

    oAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey
    oAdapter.SelectCommand = oCmd
    oAdapter.Fill(oDtSource)
    DataGridView1.DataSource = oDtSource

For refreshing I use oAdapter.Fill(oDtSource) The PrimaryKey is set in the database

回答1:

From MSDN:

You can use the Fill method multiple times on the same DataTable. If a primary key exists, incoming rows are merged with matching rows that already exist. If no primary key exists, incoming rows are appended to the DataTable.

So either define a primary key or clear the table first.

Dim table = CType(DataGridView.DataSource, DataTable)
table.Clear()
' fill  ...

To define primary key(s) manually read this. To let it create automatically if they are defined in the database you need to set the MissingSchemaAction to AddWithKey:

' ...
dataAdapter.MissingSchemaAction =  MissingSchemaAction.AddWithKey
' fill ...


回答2:

The edit code doesnt show the PrimaryKey being defined for the DataTable. This will configure the DataAdapter to perform updates and enabled refreshing the DataTable. The code uses MySQL but the Provider objects all work the same in this regard:

' persistant form level vars
Private daSample As MySqlDataAdapter
Private dtSample As DataTable
...

Elsewhere:

' there are caveats with WHERE clauses
Dim sql = "SELECT Id, Name, Country, Animal FROM SAMPLE WHERE Color = 'onyx'"

' using the ctor overload, no need for a DbCommand or Connection object
daSample = New MySqlDataAdapter(sql, MySQLConnStr)

' initialize the CommandBuilder, get other commands 
Dim cbSample = New MySqlCommandBuilder(daSample)

daSample.UpdateCommand = cbSample.GetUpdateCommand
daSample.InsertCommand = cbSample.GetInsertCommand
daSample.DeleteCommand = cbSample.GetDeleteCommand

dtSample = New DataTable()
daSample.FillSchema(dtSample, SchemaType.Source)
dtSample.PrimaryKey = New DataColumn() {dtSample.Columns("id")}

daSample.Fill(dtSample)

dgv1.DataSource = dtSample

To pick up changes made to the db from other client apps:

daSample.Fill(dtSample)

Initial display:

After I change a row to "onyx" from a UI browser and Update the changed row shows up:

WHERE clauses can be a bit of an issue. Since it restricts the subset of data pulled back, Update is only going to compare rows in the new result set. So, if I change an onlyx row to "blue" it wont be removed.

One solution is to use .DefaultView.RowFilter on the table, but that can slow things down since it requires returning all rows to the client to be filtered there. Its not perfect.