从 C# 应用程序更新 MS Access 数据库中新添加的记录时,“并发冲突:UpdateCommand 影响了预期的 1 条记录中的 0 条”

Posted

技术标签:

【中文标题】从 C# 应用程序更新 MS Access 数据库中新添加的记录时,“并发冲突:UpdateCommand 影响了预期的 1 条记录中的 0 条”【英文标题】:"Concurrency violation: the UpdateCommand affected 0 of the expected 1 records" when updating a new added record in MS Access database from C# app 【发布时间】:2015-06-02 20:30:14 【问题描述】:

请帮助解决我的问题。 尝试从我用 C# 编写的应用程序更新 MS Access(2000 格式)数据库 (.mdb) 中新添加的表记录时,出现错误“并发冲突:UpdateCommand 影响了预期的 1 条记录中的 0 条”。 这个错误是rather generic,我尝试了不同论坛上建议的解决方案,但没有成功。

这是我一步一步做的:

我在 mdb 中有一个表“TRACKS”,其中包含以下列:

ID - 输入“自动编号”(键列) 标题 - '文本' FullTitle - '文本'

长度 - '日期/时间'

    我以这种方式建立与数据库的连接并获取表记录:

    public partial class MainForm : Form
    
        public OleDbConnection dbConn = new OleDbConnection();
        public DataSet dataset = new DataSet();
    
        protected OleDbDataAdapter adTracks = new OleDbDataAdapter();
    
        protected OleDbCommandBuilder cmb;
    
        ArrayList arrArtists = new ArrayList();
    
        public MainForm(string strFileName)
        
            InitializeComponent();
            cmb = new OleDbCommandBuilder(adTracks);
        
    
        private void OnLoad(object sender, EventArgs e)
        
            dbConn.ConnectionString = Properties.Settings.Default.dbConnectionString;
            OleDbCommand cmTracks = new OleDbCommand("Select * from Tracks", dbConn);
    
            OleDbDataAdapter adapter = new OleDbDataAdapter();
    
            try
            
                dbConn.Open();
                adTracks.SelectCommand = cmTracks;
                adTracks.Fill(dataset, "Tracks");
            
            catch (Exception err)
            
                MessageBox.Show(err.Message);
                return;
            
            finally
            
                dbConn.Close();
            
    
            cboOriginal.DataSource = dataset.Tables["Tracks"];
            cboOriginal.DisplayMember = "FullTitle";
            cboOriginal.ValueMember = "ID";
            cboOriginal.SelectedIndex = -1;
    
            adTracks.RowUpdated += new OleDbRowUpdatedEventHandler(OnRowUpdated);
        
    
    

    然后我使用此代码向表中添加一条新记录(txtTitletxtGenTitle 控件包含记录的值):

    DataTable dt;
    DataRow dr;
    int newID;
    
    dt = dataset.Tables["Tracks"];
    dr = dt.NewRow();
    dr["Title"] = txtTitle.Text;
    dr["FullTitle"] = txtGenTitle.Text;
    
    dt.Rows.Add(dr);
    
    try
    
        dbConn.Open();
        adTracks.Update(dt);
    
    catch (Exception err)
    
        MessageBox.Show("Error adding new track '" + txtGenTitle.Text + "':\n" + err.Message);
        return;
    
    finally
    
        dbConn.Close();
    
    
    res = dt.Select("FullTitle = '" + txtGenTitle.Text.Replace("'", "''") + "'");
    if (res.Length != 0)
    
        newID = (int)res[0]["ID"];
    
        // continue with newID
    
    

    此代码执行成功:新记录被添加到表中,本地DataTable和mdb文件中的实际表。在此处理程序中接收到键列的新自动递增值:

    protected void OnRowUpdated(object sender, OleDbRowUpdatedEventArgs args)
    
        if (args.StatementType == StatementType.Insert)
        
            OleDbCommand idCMD = new OleDbCommand("SELECT @@IDENTITY", dbConn);
            args.Row["ID"] = (int)(idCMD.ExecuteScalar());
        
    
    

    具有此 ID 的行现在有 RowState == Unchanged,所以一切正常。

    现在我想更新这条新添加的记录中的一些值(来自txtLength 控件):

    DataTable dt;
    DataRow dr;
    DataRow[] res;
    
    dt = dataset.Tables["Tracks"];
    res = dt.Select("FullTitle = '" + txtGenTitle.Text.Replace("'", "''") + "'");
    
    if (res.Length != 0)
    
        TimeSpan tsNew = TimeSpan.Zero, tsOld = TimeSpan.Zero;
        if (txtLength.Text != String.Empty) tsNew = TimeSpan.Parse(txtLength.Text);
        if (!(res[0]["Length"] is DBNull))
        
            DateTime date = (DateTime)res[0]["Length"];
            tsOld = date.TimeOfDay;
        
    
        if (tsNew != TimeSpan.Zero && (tsOld == TimeSpan.Zero || tsOld.CompareTo(tsNew) < 0))
        
            if (tsNew != TimeSpan.Zero && (tsOld == TimeSpan.Zero || tsOld.CompareTo(tsNew) < 0)) res[0]["Length"] = txtLength.Text;
    
            if (String.Compare((string)res[0]["Title"], txtTitle.Text, true) != 0)
            
                res[0]["Title"] = txtTitle.Text;
                res[0]["FullTitle"] = txtGenTitle.Text;
            
    
            try
            
                dbConn.Open();
                adTracks.Update(dt);
            
            catch (Exception err)
            
                MessageBox.Show("Error updating track '" + txtGenTitle.Text + "':\n" + err.Message);
                return;
            
            finally
            
                dbConn.Close();
            
        
    
    

    并在adTracks.Update(dt) 行出现错误“并发冲突:UpdateCommand 影响了预期的 1 条记录中的 0 条”。数据库未更新,DataTable 也未更新。

这可能意味着记录 ID 存在一些错误 - 插入后它没有更新为正确的值。但这不是这里的情况: ID 在步骤 2 的 OnRowUpdated 处理程序中更新为正确的 ID,并且具有此 ID 的记录也被添加到表 mdb 文件中。 在调用adTracks.Update 之前的第3 步,res[0] 还包含正确的ID 值和RowState == Modified。但我仍然得到这个错误。我做错了什么?

按照建议的here 在adTracks.Update(dt) 之后添加dt.AcceptChanges() - 没有帮助。


更新:

1. krish建议的尝试方法:

我在第 3 步的 try/catch 块之前添加了以下几行:

string cmd = "UPDATE TRACKS SET Length = '" + res[0]["Length"] + "' WHERE ID = " + res[0]["ID"];
adTracks.UpdateCommand = new OleDbCommand(cmd, dbConn);

而且它有效!数据库已更新,对应的DataRow 得到RowState == Unchanged。 这是一个很好的解决方法。但我仍然想知道为什么“传统”方法在这里不起作用。当需要更新许多列时,解决方法不是很方便。此外,它似乎只有在我更新单行时才可以接受,并且我需要一次更新多行的能力。

2。 hynsey建议的尝试方法:

我将第 3 步中的 adTracks.Update(dt); 行替换为以下代码:

using (OleDbDataAdapter da = new OleDbDataAdapter ("Select * from Tracks", dbConn))

    OleDbCommandBuilder cb = new OleDbCommandBuilder(da);
    da.RowUpdated += new OleDbRowUpdatedEventHandler(OnRowUpdated);
    da.Update(dt);

遗憾的是,行为根本没有改变 - 相同的错误“并发冲突:UpdateCommand 影响了预期的 1 条记录中的 0 条”。

这是我在所有 3 个步骤中使用的代码(与原始代码比较):

1. 
        public partial class MainForm : Form
        
            public OleDbConnection dbConn = new OleDbConnection();
            public DataSet dataset = new DataSet();

            protected OleDbDataAdapter adTracks = new OleDbDataAdapter();

            ArrayList arrArtists = new ArrayList();

            public MainForm(string strFileName)
            
                InitializeComponent();
            

            private void OnLoad(object sender, EventArgs e)
            
                dbConn.ConnectionString = Properties.Settings.Default.dbConnectionString;

                try
                
                    dbConn.Open();
                    adTracks = new OleDbDataAdapter("Select * from Tracks", dbConn));
                    adTracks.Fill(dataset,"Tracks");    
                
                catch (Exception err)
                
                    MessageBox.Show(err.Message);
                    return;
                
                finally
                
                    dbConn.Close();
                

                cboOriginal.DataSource = dataset.Tables["Tracks"];
                cboOriginal.DisplayMember = "FullTitle";
                cboOriginal.ValueMember = "ID";
                cboOriginal.SelectedIndex = -1;

                adTracks.RowUpdated += new OleDbRowUpdatedEventHandler(OnRowUpdated);
            
        

2.
        DataTable dt;
        DataRow dr;
        int newID;

        dt = dataset.Tables["Tracks"];
        dr = dt.NewRow();
        dr["Title"] = txtTitle.Text;
        dr["FullTitle"] = txtGenTitle.Text;

        dt.Rows.Add(dr);

        try
        
            dbConn.Open();
            using (OleDbDataAdapter da = new OleDbDataAdapter ("Select * from Tracks", dbConn)) 
            
                OleDbCommandBuilder cb = new OleDbCommandBuilder(da); 
                da.RowUpdated += new OleDbRowUpdatedEventHandler(OnRowUpdated);
                da.Update(dataset, "Tracks"); 
            
        
        catch (Exception err)
        
            MessageBox.Show("Error adding new track '" + txtGenTitle.Text + "':\n" + err.Message);
            return;
        
        finally
        
            dbConn.Close();
        

        res = dt.Select("FullTitle = '" + txtGenTitle.Text.Replace("'", "''") + "'");
        if (res.Length != 0)
        
            newID = (int)res[0]["ID"];

            // continue with newID
                

3.
        DataTable dt;
        DataRow dr;
        DataRow[] res;

        dt = dataset.Tables["Tracks"];
        res = dt.Select("FullTitle = '" + txtGenTitle.Text.Replace("'", "''") + "'");

        if (res.Length != 0)
        
            TimeSpan tsNew = TimeSpan.Zero, tsOld = TimeSpan.Zero;
            if (txtLength.Text != String.Empty) tsNew = TimeSpan.Parse(txtLength.Text);
            if (!(res[0]["Length"] is DBNull))
            
                DateTime date = (DateTime)res[0]["Length"];
                tsOld = date.TimeOfDay;
            

            if (tsNew != TimeSpan.Zero && (tsOld == TimeSpan.Zero || tsOld.CompareTo(tsNew) < 0))
            
                if (tsNew != TimeSpan.Zero && (tsOld == TimeSpan.Zero || tsOld.CompareTo(tsNew) < 0)) res[0]["Length"] = txtLength.Text;

                if (String.Compare((string)res[0]["Title"], txtTitle.Text, true) != 0)
                
                    res[0]["Title"] = txtTitle.Text;
                    res[0]["FullTitle"] = txtGenTitle.Text;
                

                try
                
                    dbConn.Open();
                    using (OleDbDataAdapter da = new OleDbDataAdapter ("Select * from Tracks", dbConn)) 
                    
                        OleDbCommandBuildercb = new OleDbCommandBuilder(da); 
                        da.Update(dataset , "Tracks"); 
                    
                
                catch (Exception err)
                
                    MessageBox.Show("Error updating track '" + txtGenTitle.Text + "':\n" + err.Message);
                    return;
                
                finally
                
                    dbConn.Close();
                
            
        

3.为了调查为什么我的初始代码不起作用,我提供了有关错误的更多详细信息:

OnRowUpdated 处理程序被调用,尽管有错误,我能够检查传递给处理程序的args 参数。 args.Row 具有 RowState == Modifiedargs.Command 具有以下 CommandText(我添加了换行符以提高可读性):

UPDATE Tracks SET Length = ? WHERE ((ID = ?) AND
((? = 1 AND Title IS NULL) OR (Title = ?)) AND
((? = 1 AND FullTitle IS NULL) OR (FullTitle = ?)) AND
((? = 1 AND GenreID IS NULL) OR (GenreID = ?)) AND
((? = 1 AND StyleID IS NULL) OR (StyleID = ?)) AND
((? = 1 AND SubStyleID IS NULL) OR (SubStyleID = ?)) AND
((? = 1 AND Length IS NULL) OR (Length = ?)) AND
((? = 1 AND UseOriginal IS NULL) OR (UseOriginal = ?))
AND ((? = 1 AND Version IS NULL) OR (Version = ?)) AND
((? = 1 AND TrackID IS NULL) OR (TrackID = ?)) AND
((? = 1 AND SpecPresConjunctor IS NULL) OR (SpecPresConjunctor = ?)) AND
((? = 1 AND SpecFeatConjunctor IS NULL) OR (SpecFeatConjunctor = ?)) AND
((? = 1 AND FreeRecord IS NULL) OR (FreeRecord = ?)))

谁能告诉这个生成的命令有什么问题? “GenreID”、“StyleID”等是“TRACKS”表中的其他列。我不知道所有这些“?”意思。

另外,当异常发生时,堆栈上的最新调用如下:

at System.Data.Common.DbDataAdapter.UpdatedRowStatusErrors(RowUpdatedEventArgs rowUpdatedEvent, BatchCommandInfo[] batchCommands, Int32 commandCount)
at System.Data.Common.DbDataAdapter.UpdatedRowStatus(RowUpdatedEventArgs rowUpdatedEvent, BatchCommandInfo[] batchCommands, Int32 commandCount)
at System.Data.Common.DbDataAdapter.Update(DataRow[] dataRows, DataTableMapping tableMapping)
at System.Data.Common.DbDataAdapter.UpdateFromDataTable(DataTable dataTable, DataTableMapping tableMapping)
at System.Data.Common.DbDataAdapter.Update(DataTable dataTable)
at *my code as above*

【问题讨论】:

查看本站:blogs.msdn.com/b/spike/archive/2010/04/07/… 在使用 DataAdapters 更新数据集时,我倾向于使用以下方法:using (OleDbDataAdapter da = new OleDbDataAdapter ("Select * from Tracks", dbConn)) OracleCommandBuilder cb = new OracleCommandBuilder(adTracks); adTracks.Update(dataset , dataset.Tables["Tracks"]); @krish 我知道这个链接。在我的情况下,没有并发,我的应用程序是唯一一次访问数据库并且没有记录被删除的应用程序。因此,遗憾的是,该文章中描述的情况不适用于此处。 你能试试标准的 SQL 执行吗?对数据库的字符串更新命令 @krish 你的意思是放弃 OleDbCommandBuilder 并自己创建更新命令(“Update Tracks Set ...”)吗? 【参考方案1】:

用这个替换你的 OnLoad 方法:

    private void OnLoad(object sender, EventArgs e)
    
        dbConn.ConnectionString = Properties.Settings.Default.dbConnectionString;

    try
    
        dbConn.Open();
        adTracks = new OleDbDataAdapter("Select * from Tracks", dbConn));
        adTracks.Fill(dataset,"Tracks");    
    
    catch (Exception err)
    
        MessageBox.Show(err.Message);
        return;
    
    finally
    
        dbConn.Close();
    

    cboOriginal.DataSource = dataset.Tables["Tracks"];
    cboOriginal.DisplayMember = "FullTitle";
    cboOriginal.ValueMember = "ID";
    cboOriginal.SelectedIndex = -1;

    adTracks.RowUpdated += new OleDbRowUpdatedEventHandler(OnRowUpdated);

然后是更新数据集的代码: (请注意,我更正了上面评论中对adTracks 的错误引用。)

using (OleDbDataAdapter da = new OleDbDataAdapter ("Select * from Tracks", dbConn)) 

OracleCommandBuilder cb = new OracleCommandBuilder(da); 
da.Update(dataset , "Tracks"); 

【讨论】:

对不起,还是一样的结果。我还用上面的 sn-p 替换了第 2 步中的 adTracks.Update(dt);using (OleDbDataAdapter da = new OleDbDataAdapter ("Select * from Tracks", dbConn)) OracleCommandBuilder cb = new OracleCommandBuilder(da); da.Update(dataset , "Tracks"); ,但这也没有帮助。 顺便说一句,使用 OnLoad 方法的代码原样,在第 2 行的第 adTracks.Update(dt); 行上出现错误:“当传递带有新行的 DataRow 集合时,更新需要有效的 InsertCommand ".所以我不得不在 OnLoad 的 finally 子句之后添加这一行:cmb1.DataAdapter = adTracks;。然后我更新了第 2 步中的代码,如先前评论中所述 - 结果仍然相同。 我在上面的代码中没有看到任何对cmb1 的引用?这是什么? 对不起,应该是:cmb.DataAdapter = adTracks; 您在评论中声明“顺便说一句,使用您的 OnLoad 方法代码,在 adTracks.Update(dt); 行的第 2 步出现错误”。但是,我的解决方案没有引用adTracks。删除您所做的更改,并使用我的方法(在我的解决方案中的粗体文本下详细说明)更新数据适配器。让我知道情况如何。 (注意我的编辑)【参考方案2】:

在并发方面有同样的问题。没有任何效果。 2 天后,我看到了这篇关于 Ms Access 和数据类型小数的文章。 本文指出应避免使用此数据类型。

所以我使用数据类型 Double 重新设计了表格。用新名称保存它。复制了数据,最后用旧名重命名了新表。

在 Visual Studio 中,我删除了 Dataset.xsd 并添加了一个新数据集

终于成功了。

请阅读: https://www.fmsinc.com/microsoftaccess/database-design/decimal_data_type/index.htm

【讨论】:

以上是关于从 C# 应用程序更新 MS Access 数据库中新添加的记录时,“并发冲突:UpdateCommand 影响了预期的 1 条记录中的 0 条”的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 SQL 命令从我的 DataTable 对象中使用 foreach-loop 更新 C# 中的 MS Access 数据库?

如何从 C# 调用 MS Access 数据库宏

在 C# 中更新 MS Access 表

如何在 C# 中更新或刷新与 Ms-access 连接的数据网格视图

c# - 从 ms access 数据库中随机生成数据

C# Ms-access 从数据库中获取详细信息