You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# SQLite使用SqliteDataAdapter更新BindingList时触发UNIQUE约束失败

问题分析与解决方案

你遇到的核心问题是:当你把BindingList<Proxy>转成DataTable时,所有行的RowState都是Added,所以SqliteDataAdapter会对每一行执行INSERT操作,而非你预期的UPDATE——这就触发了唯一约束(因为这些数据本来就在数据库里,主键重复了)。

为什么会这样?你的ToDataTable扩展方法是从头创建新的DataRow并添加到DataTable里,这些新行的默认状态就是Added,适配器根本不知道这些是已经存在的修改后的数据,只会当新数据处理。

正确的实现思路:让BindingList与DataTable保持绑定,跟踪行状态

最可靠的方式是不要每次保存都重新转换DataTable,而是在加载数据时就保留原始的DataTable,通过BindingSource让BindingList和这个DataTable关联起来。这样你修改BindingList中的数据时,对应的DataRow状态会自动被标记为Modified(或者Deleted/Added),后续调用adapter.Update()时,适配器就能正确判断该执行什么SQL命令。

修改后的代码示例

首先,在类里添加成员变量保存原始的DataTable和关联对象:

private DataTable _proxyTable;
private BindingSource _bindingSource;
private BindingList<Proxy> _proxies;

然后修改LoadDatabase方法:

public void LoadDatabase()
{
    using (var conn = new SQLiteConnection(dbConnectionString))
    {
        using (var adapter = new SQLiteDataAdapter("SELECT * FROM proxies", conn))
        {
            _proxyTable = new DataTable();
            adapter.Fill(_proxyTable);
            
            // 用BindingSource关联DataTable和BindingList,修改会自动同步
            _bindingSource = new BindingSource();
            _bindingSource.DataSource = _proxyTable;
            
            _proxies = (BindingList<Proxy>)_bindingSource.List;
        }
    }
}

SaveDatabase方法直接用原始DataTable更新:

public void SaveDatabase()
{
    using (var conn = new SQLiteConnection(dbConnectionString))
    {
        using (var adapter = new SQLiteDataAdapter("SELECT * FROM proxies", conn))
        {
            using (var builder = new SQLiteCommandBuilder(adapter))
            {
                // SqliteCommandBuilder会自动生成对应命令,前提是表有主键
                adapter.Update(_proxyTable);
            }
        }
    }
}

关键注意事项

  1. 确保表有主键:SqliteCommandBuilder只有在查询结果包含主键列时,才能正确生成UPDATE和DELETE命令。如果你的proxies表没有主键,建议添加类似Id INTEGER PRIMARY KEY AUTOINCREMENT的主键字段。
  2. 避免手动转换DataTable:通过BindingSource关联后,BindingList的修改会自动同步到DataTable,不需要手动来回转换,既减少错误又提升效率。
  3. 不要清空表再插入:你提到的DELETE * from the table是非常不合理的做法——不仅效率低下,还会丢失数据状态,完全没必要。

若坚持使用转换扩展方法(不推荐)

如果因特殊需求必须用BindingList转DataTable的方式,你需要在转换时手动设置DataRow的状态为Modified,且确保每行都包含主键值。但这种方式需要自行跟踪数据修改状态,实现复杂且易出错,因此优先推荐上述绑定方案。

内容的提问来源于stack exchange,提问作者JohnWick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:55:23