0

我有一个显示记录列表的 DataGridView,当我点击插入按钮时,表单应该添加一条新记录,编辑它的值并保存它。

我有一个绑定到 DataGridView 的 BindingSource。我将作为参数传递给 NEW RECORD 表单,所以

// When the form opens it add a new row and de DataGridView display this new record at this time
DataRowView currentRow;
currentRow = (DataRowView) myBindindSource.AddNew();

当用户确认保存它时,我会做一个

myBindindSource.EndEdit(); // inside the form

并在处理表单后保存新行并将绑定位置更新为新行

DataRowView drv = myForm.CurrentRow;
avaliadoTableAdapter.Update(drv.Row);
avaliadoBindingSource.Position = avaliadoBindingSource.Find("ID", drv.Row.ItemArray[0]);

问题是该表有一个 AUTOINCREMENT 字段,并且保存的值可能与 bindingSource 在 EDIT TIME 中给出的值不对应。

因此,当我再次关闭并打开 DataGridView 时,新的 rowd 给出其 ID 是基于当时 undelying DB 中的可用槽位被保存,它只是忽略 BindingSource 生成的广告编辑时间的值,

由于绑定源给出的值应该被另一个表用作foreingKey,它使引用不一致。

有没有办法让真实的ID被保存到数据库中?

4

6 回答 6

2

我想出了这个解决方案

首先在表模型中直接添加了一个 GetNextID() 方法:

SELECT     autoinc_next
FROM         information_schema.columns
WHERE     (table_name = 'Estagio') AND (column_name = 'ID')

当我需要添加新行时,我会这样做

 EstagioTableAdapter ta = new EstagioTableAdapter ();
 nextID = ta.GetNextID();

 row = (DataRowView)source.AddNew();
 row.Row["ID"] = nextID;
 (...)
 source.EndEdit();
于 2010-01-02T23:12:02.787 回答
0

Access 数据库也会发生同样的事情。这里有一篇很棒的文章(有解决方案)。基本上,当您保存数据时,TableAdapter 通常会批量发送 2 个查询。第一个保存数据,第二个请求新 ID。不幸的是,Access 和 SQL CE 都不支持批处理语句。

解决方案是为 RowUpdated 添加一个事件处理程序,该处理程序在数据库中查询新 ID。

于 2009-11-19T03:22:52.507 回答
0

根据我对并发冲突的回答,使用 da.InsertCommand。UpdatedRowSource = UpdateRowSource.FirstReturnedRecord。

注意:只需将 SQLiteConnection 和 SQLiteDataAdapter 更改为 MSSQL ,并将 LAST_INSERT_ROWID() 更改为SCOPE_IDENTITY ()

    const string devMachine = @"Data Source=C:\_DEVELOPMENT\__.NET\dotNetSnippets\Mine\TestSqlite\test.s3db";

    SQLiteConnection c = new SQLiteConnection(devMachine);
    SQLiteDataAdapter da = new SQLiteDataAdapter();
    DataTable dt = new DataTable();

    public Form1()
    {
        InitializeComponent();


        da = new SQLiteDataAdapter("select product_id, product_name, abbrev from product", c);

        var b = new SQLiteCommandBuilder(da);

        da.InsertCommand = new SQLiteCommand(
            @"insert into product(product_id, product_name, abbrev) values(:_product_id, :_product_name, :_abbrev);
            select product_id /* include rowversion field here if you need */ 
            from product where product_id = LAST_INSERT_ROWID();", c);
        da.InsertCommand.Parameters.Add("_product_id", DbType.Int32,0,"product_id");
        da.InsertCommand.Parameters.Add("_product_name", DbType.String, 0, "product_name");
        da.InsertCommand.Parameters.Add("_abbrev", DbType.String, 0, "abbrev");
        da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;

        da.UpdateCommand = b.GetUpdateCommand();
        da.DeleteCommand = b.GetDeleteCommand();

        da.Fill(dt);


        bds.DataSource = dt;
        grd.DataSource = bds;

    }

    private void uxUpdate_Click(object sender, EventArgs e)
    {
        da.Update(dt);
    }

这是 SQLite 上的示例表:

CREATE TABLE [product] (
[product_id] INTEGER  NOT NULL PRIMARY KEY AUTOINCREMENT,
[product_name] TEXT  NOT NULL,
[abbrev] TEXT  NOT NULL
)

[编辑 2009 年 11 月 19 日 12:58 PM CN]嗯...我想我的答案不能用,SQLCE 不允许多个语句

无论如何,当您使用基于服务器的 MSSQL 或使用 SQLite 时,只需使用我的答案。或者,也许,将这两个语句封装到一个返回 scope_identity(integer) 的函数中:

da.InsertCommand = new SQLiteCommand(
@"select insert_to_product(:_product_id, :_product_name, :_abbrev) as product_id", c);
da.InsertCommand.Parameters.Add("_product_id", DbType.Int32,0,"product_id");
da.InsertCommand.Parameters.Add("_product_name", DbType.String, 0, "product_name");
da.InsertCommand.Parameters.Add("_abbrev", DbType.String, 0, "abbrev");
da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;
于 2009-11-19T04:50:47.290 回答
0

注意:只需将 SQLiteConnection 和 SQLiteDataAdapter 更改为 MSSQL ,并将 LAST_INSERT_ROWID() 更改为SCOPE_IDENTITY ()

使用RowUpdated(应在不支持多语句的SQLCE和 RDBMS 上工作):

const string devMachine = @"Data Source=C:\_DEVELOPMENT\__.NET\dotNetSnippets\Mine\TestSqlite\test.s3db";

SQLiteConnection c = new SQLiteConnection(devMachine);
SQLiteDataAdapter da = new SQLiteDataAdapter();
DataTable dt = new DataTable();

public Form1()
{
    InitializeComponent();


    da = new SQLiteDataAdapter("select product_id, product_name, abbrev from product", c);

    var b = new SQLiteCommandBuilder(da);

    da.InsertCommand = b.GetInsertCommand();
    da.UpdateCommand = b.GetUpdateCommand();
    da.DeleteCommand = b.GetDeleteCommand();

    da.Fill(dt);


    da.RowUpdated += da_RowUpdated;


    bds.DataSource = dt;
    grd.DataSource = bds;



}

void da_RowUpdated(object sender, System.Data.Common.RowUpdatedEventArgs e)
{
    if (e.StatementType == StatementType.Insert)
    {
        int ident = (int)(long) new SQLiteCommand("select last_insert_rowid()", c).ExecuteScalar();
        e.Row["product_id"] = ident;
    }
}

private void uxUpdate_Click(object sender, EventArgs e)
{
    da.Update(dt);
}
于 2009-11-19T06:09:04.120 回答
0

我没有机会使用 SQLiteConnection 类,但我确实使用了 SQLConnection 和 SQLCommand 类。SqlCommand 有一个 ExecuteScalar 方法,它返回 t-sql 语句的第一行和第一列的值。您可以使用它返回 Auto-Identity 列。此外,在 SQL Server 2005 中有一个名为 OUTPUT 的关键字,您也可以检查它。

于 2009-11-19T06:58:44.130 回答
0

我遇到过这个问题:您需要做的就是将您的自动增量种子设置为 -1 并将其“增量”为 -1。这样,您的所有数据行都将具有唯一的 ID,这些 ID 不会映射到真实数据库中的任何内容。如果您使用 a 保存数据DataAdapter,那么在保存后您的数据行和任何其他具有指向该 ID 的数据关系的行都将被更新

于 2009-11-24T13:11:50.123 回答