首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > .NET > C# >

在datatable中新增记要后,马上修改,就会报错

2013-09-17 
在datatable中新增记录后,马上修改,就会报错在datatable中新增记录后,马上修改,就会报如下错误“违反并发性

在datatable中新增记录后,马上修改,就会报错
在datatable中新增记录后,马上修改,就会报如下错误
“违反并发性: UpdateCommand 影响了预期 1 条记录中的 0 条。”
如果修改旧的数据则不报错。

Material表创建语句
CREATE TABLE [Material] (
[i] [int] IDENTITY (1, 1) NOT NULL ,
[ID] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL ,
[Name] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NULL ,
CONSTRAINT [PK_Material] PRIMARY KEY  CLUSTERED 
(
[ID]
)  ON [PRIMARY] 
) ON [PRIMARY]


代码如下

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;

namespace testUpdate
{
    public partial class Form1 : Form
    {
        private SqlConnection nSqlcon;
        private DataSet nds;
        private DataTable ndt;
        private DataRow ndr;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {   
            //打开表
            string strSql="Server=.;Database=weight;Uid=sa;Pwd=123";
            nSqlcon = new SqlConnection(strSql);
            nSqlcon.Open();

            strSql = "select * from material order by id";
            SqlDataAdapter sda = new SqlDataAdapter(strSql, nSqlcon);
            nds = new DataSet();
            sda.Fill(nds);
            ndt= nds.Tables[0];


            dataGridView1.DataSource = ndt; 
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //新增一条记录
            ndr = ndt.NewRow();
            ndr["ID"] = "1001";
            ndr["Name"] = "TEST";
            ndt.Rows.Add(ndr);
            SqlDataAdapter sda = new SqlDataAdapter();
            SqlCommand scmd = new SqlCommand("select * from material order by id", nSqlcon);
            sda.SelectCommand = scmd;
            SqlCommandBuilder scb = new SqlCommandBuilder(sda);
            sda.Update(ndt);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            //修改一条记录
            ndr = ndt.Rows[dataGridView1.SelectedRows[0].Index];
            ndr.BeginEdit();
            ndr["Name"] = "TEST2";
            ndr.EndEdit();
            SqlDataAdapter sda = new SqlDataAdapter();
            SqlCommand scmd = new SqlCommand("select * from material order by id", nSqlcon);
            sda.SelectCommand = scmd;
            SqlCommandBuilder scb = new SqlCommandBuilder(sda);
            sda.Update(ndt);


        }
    }
}
数据 并发
[解决办法]
新增每条记录,需要程序先判断datatable中是否主键字段已有该值,可用table.select判断,然后读取判断结果的length是否为0,如果没有该值出现,则可以新增一条记录,并赋值主键,然后update。
[解决办法]
原因是:第一次新增时,本机内存中的datatable把新增记录各字段值update给 SQL Server, SQL Server 保存同时自动赋值id,但是该id并没有返回内存中的datatable,因此datatable中的这条记录是没有主键值的,此时如果程序又要修改该条记录,并想再次提交保存,此时 SQL Server 接到该记录的修改信息,但里面没有主键值, SQL Server 无法定位该条记录(这和第一次新增记录不同,新增不需要去定位记录位置),也就无法保存这1条记录,从而报错:无法找到应该有的1条中的0条。
[解决办法]
这样写更新代码很少看,不知道楼主这样的代码从哪里来?为何不写一个update语句直接提交更新数据呢?

sql="update tb set name='TEST2' where name='TEST'"

你那样写你不好检查,别人也不好检查


[解决办法]
新增,提交。然后再修改,在提交。

如果你刚刚新增的数据不保存到数据就要立刻修改,那么你为什么不直接新增这修改过的值呢?
[解决办法]
更新代码, 只有 DataTable 一条路吗?

感觉死死的, 一点都不灵活。

如果大批量的数据, 简单的数据修改, 用用还可以, 

搞得复杂了, 还不如果换用其它的
[解决办法]

引用:
Quote: 引用:

更新代码, 只有 DataTable 一条路吗?

感觉死死的, 一点都不灵活。

如果大批量的数据, 简单的数据修改, 用用还可以, 

搞得复杂了, 还不如果换用其它的


现在的问题不是用哪种方法,而是这种方法为何会报错,如何修改。


没有必要在人为复杂的世界里打滚, 能简化就简化吧
[解决办法]
用没有用到事务,你提交到数据库了么?

[解决办法]
引用:
原因是:第一次新增时,本机内存中的datatable把新增记录各字段值update给 SQL Server, SQL Server 保存同时自动赋值id,但是该id并没有返回内存中的datatable,因此datatable中的这条记录是没有主键值的,此时如果程序又要修改该条记录,并想再次提交保存,此时 SQL Server 接到该记录的修改信息,但里面没有主键值, SQL Server 无法定位该条记录(这和第一次新增记录不同,新增不需要去定位记录位置),也就无法保存这1条记录,从而报错:无法找到应该有的1条中的0条。


就是这个原因,可以通过DataAdapter的RowUpdated的事件,通过SCOPE_IDENTITY()这个SQL函数获取数据库创建的ID 然后传DATATABLE中的IDENTITY 就可以了
以下是MSDN事例代码

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]);
            }
        }
    }
}

热点排行