C#System.Data.SQLite:并发冲突:UpdateCommand影响了预期的1条记录中的0条

时间:2016-05-03 05:22:10

标签: c# sqlite concurrency sql-update

我有一个使用SQLite DB的.net winforms应用程序,我正在使用System.Data.SQLite dll。

这是我加载数据表的方式:

            DataTable table = new DataTable();
            SQLiteDataAdapter m_readingsDataTableDataAdapter;
            SQLiteCommandBuilder m_readingsDataTableCommandBuilder;

            m_readingsDataTableCommandBuilder = null;
            m_readingsDataTableDataAdapter = null;

            // Create fresh objects
            m_readingsDataTableDataAdapter = new SQLiteDataAdapter(sql, Database.getInstance().connectionObj);
            m_readingsDataTableCommandBuilder = new SQLiteCommandBuilder(m_readingsDataTableDataAdapter);
            m_readingsDataTableDataAdapter.Fill(table);
            return table;             

此数据表有一个主键,没有其他约束。 我将其设置为DataGridView的数据源,在完成所有编辑后,我更新了DataTable,如下所示:

    m_readingsDataTableDataAdapter.Update(table);

有时,更新会触发错误,但我不知道何时停止抛出错误 - 可能是在系统重启后(不确定)。然后,更新正常,直到此更新再次引发错误的另一种情况。发生错误时,即使在应用程序重新启动后,也会对所有更新进行更新。

错误: 类型为#System; Data.Data.DBConcurrencyException'的未处理异常发生在System.Data.dll中 附加信息:并发冲突:UpdateCommand影响了预期的1条记录中的0条。

我很感激任何帮助或问题,因为这是我项目的一个重要部分。

感谢。

更新

根据建议,为了确保程序的其他部分没有编辑行,我加载了DataTable,立即更新了行并按照以下代码更新了它。 我仍然收到错误。我的机器上没有运行其他程序,因此除了我的应用程序之外,任何其他程序都不会更新该行:

    DataTable table = new DataTable();
            SQLiteDataAdapter m_readingsDataTableDataAdapter;
            SQLiteCommandBuilder m_readingsDataTableCommandBuilder;

            m_readingsDataTableCommandBuilder = null;
            m_readingsDataTableDataAdapter = null;

            // Create fresh objects
            m_readingsDataTableDataAdapter = new SQLiteDataAdapter(sql, Database.getInstance().connectionObj);
            m_readingsDataTableCommandBuilder = new SQLiteCommandBuilder(m_readingsDataTableDataAdapter);
            m_readingsDataTableDataAdapter.Fill(table);

            // Update immediately
            table.Rows[0]["RS485_ADDRESS"] = "400";
            m_readingsDataTableDataAdapter.Update(table); // - Still throws error

            return table;

2 个答案:

答案 0 :(得分:1)

我有一个类似的问题让我差点疯了。我甚至调试了System.Data.SQLite。我的问题是由SQLite的动态类型引起的: 动态类型意味着列类型只是存储值类型的提示,而不是强制执行。以下是我的应用程序中发生的情况:整数列包含空字符串。获取数据会触发隐式转换为列类型,因此空字符串转换为零。 DbDataAdapter在UPDATE或DELETE语句的WHERE子句中使用此零值,该语句失败,因为零到字符串的转换不是空字符串。我将受影响的整数列更改为包含NULL,现在一切正常。

关于这一点的可悲事实:它不是SQLite或ADO.NET中的错误。 DbAdapter和DbCommandBuilder期望强类型,而SQLite不提供设计。

答案 1 :(得分:0)

我已经看到了这个问题,它似乎出现在某些情况下,而不是其他情况。我以不同的方式解决了它。由于这主要是数据问题,因此涉及更改更新和删除语句。可能存在您的代码可能需要验证的情况,我没有提到这些情况,这是因为其他所有情况似乎都是有序的并且"并发冲突"仍然存在。在花费数小时调试之前,一个好的起点就是解决如何处理更新。

打开数据集设计器 单击表适配器。 在属性中,展开UpdateCommand

编辑SQL,使Where-Clause仅指主键。

示例:假设EmployeeID列是表中的主键 改为:

更新..... WHERE EmployeeID = @ EmployeeID

默认情况下,它通常构造为:

更新...其中ID = @ ID AND col1 = @ col1 AND col2 = @ col2 AND col3 = @ col3

where子句由所有列组成,用于处理多用户更新,以强制执行隐式并发。因此,如果一个用户拥有该记录的副本,而另一个用户在第一个用户更新该版本时更新该版本,则第一个用户的版本不是最新版本。否则,应使用主键来引用特定记录。

但是,这是开发和数据库团队需要协调的更新/删除策略决策。对于单个开发人员/ dba in-one,请与自己进行一次小型会议。如果没有您的审查和理解以及明确的实施,请不要使用Microsoft where子句。需要专门解决2个用户从应用程序和数据库级别获得相同记录副本时发生的情况。数据库"交易隔离" SQL Server中的级别是解决它的一种方法。 ADO,ADO.NET,OLE DB,ODBC都具有隔离级别设置。 More on SQL Server Transactions and isolation levels

在某些情况下,不涉及多个用户,或者应用程序在内部更新,并发性完全受控,where子句可以使用主键形成。

在我的情况下,更新是在没有用户参与的情况下完成的,因此使用主键是有意义的。如果您了解行为的原因和原因,则需要根据您的环境进行调整。