C# Sqlite '约束失败'

Posted

技术标签:

【中文标题】C# Sqlite \'约束失败\'【英文标题】:C# Sqlite 'constraint failed'C# Sqlite '约束失败' 【发布时间】:2018-05-10 06:52:30 【问题描述】:

我有一个 BindingList,它是 DataGridView 的数据源。我希望能够使用 SqliteDataAdapter + SqliteCommandBuilder 来简化加载/保存到我的 Sqlite 数据库,但是因为 SqliteDataAdapter.Fill/Update 都只将 DataTable 或 DataSet 作为参数,所以我正在使用扩展方法将我的 BindingList 转换为 DataTable。

执行此操作时,如果我更新 BindingList 中的某些值,然后尝试更新数据库,我会收到 UNIQUE 约束失败异常。我不太确定为什么我的 SqliteDataAdapter 尝试执行 INSERT 而不是更新,考虑到没有添加任何值,只是更新了。

我可以在调用 adapter.Update 之前DELETE * from the table,但这似乎并不理想或不正确。

实现我的目标的正确方法是什么(能够使用带有通用列表的 adapter.Fill/Update)?

public void LoadDatabase()

    using (var conn = new SQLiteConnection(dbConnectionString))
    
        using (var adapter = new SQLiteDataAdapter("SELECT * FROM proxies", conn))
        
            var table = new DataTable();
            adapter.Fill(table);
            var list = new BindingList<Proxy>(table.ConvertTo<Proxy>());
            foreach (var item in list)
            
                proxies.Add(item);
            
        
    


public void SaveDatabase()

    using (var conn = new SQLiteConnection(dbConnectionString))
    
        using (var adapter = new SQLiteDataAdapter("SELECT * FROM proxies", conn))
        
            using (var builder = new SQLiteCommandBuilder(adapter))
            
                adapter.InsertCommand = builder.GetInsertCommand();
                adapter.DeleteCommand = builder.GetDeleteCommand();
                adapter.UpdateCommand = builder.GetUpdateCommand();
                var table = proxies.ToDataTable();
                adapter.Update(table);

            
        
    

使用的扩展方法:

public static List<T> ConvertTo<T>(this DataTable datatable) where T : new()

    List<T> Temp = new List<T>();
    try
    
        List<string> columnsNames = new List<string>();
        foreach (DataColumn DataColumn in datatable.Columns)
            columnsNames.Add(DataColumn.ColumnName);
        Temp = datatable.AsEnumerable().ToList().ConvertAll<T>(row => getObject<T>(row, columnsNames));
        return Temp;
    
    catch
    
        return Temp;
    


public static T getObject<T>(DataRow row, List<string> columnsName) where T : new()

    T obj = new T();
    try
    
        string columnname = "";
        string value = "";
        PropertyInfo[] Properties;
        Properties = typeof(T).GetProperties();
        foreach (PropertyInfo objProperty in Properties)
        
            columnname = columnsName.Find(name => name.ToLower() == objProperty.Name.ToLower());
            if (!string.IsNullOrEmpty(columnname))
            
                value = row[columnname].ToString();
                if (!string.IsNullOrEmpty(value))
                
                    if (objProperty.PropertyType.IsEnum)
                    
                        objProperty.SetValue(obj, Enum.Parse(objProperty.PropertyType, value));
                    
                    else
                    
                        if (Nullable.GetUnderlyingType(objProperty.PropertyType) != null)
                        
                            value = row[columnname].ToString().Replace("$", "").Replace(",", "");
                            objProperty.SetValue(obj, Convert.ChangeType(value, Type.GetType(Nullable.GetUnderlyingType(objProperty.PropertyType).ToString())), null);
                        
                        else
                        
                            value = row[columnname].ToString().Replace("%", "");
                            objProperty.SetValue(obj, Convert.ChangeType(value, Type.GetType(objProperty.PropertyType.ToString())), null);
                        
                    
                
            
        
        return obj;
    
    catch
    
        return obj;
    


public static DataTable ToDataTable<T>(this IList<T> data)

    PropertyDescriptorCollection properties =
        TypeDescriptor.GetProperties(typeof(T));
    DataTable table = new DataTable();
    foreach (PropertyDescriptor prop in properties)
        table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
    foreach (T item in data)
    
        DataRow row = table.NewRow();
        foreach (PropertyDescriptor prop in properties)
            row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
        table.Rows.Add(row);
    
    return table;

【问题讨论】:

【参考方案1】:

我决定在执行 adapter.Update() 之前删除所有记录,这是我能想到的最简单的解决方案。如果有人有更好的想法,请告诉。

public void SaveDatabase()

    using (var conn = new SQLiteConnection(dbConnectionString))
    
        using (var adapter = new SQLiteDataAdapter("SELECT * FROM proxies", conn))
        
            using (var builder = new SQLiteCommandBuilder(adapter))
            
                conn.Open();
                using (var cmd = new SQLiteCommand("DELETE FROM proxies", conn))
                
                    cmd.ExecuteNonQuery();
                
                adapter.InsertCommand = builder.GetInsertCommand();
                adapter.DeleteCommand = builder.GetDeleteCommand();
                adapter.UpdateCommand = builder.GetUpdateCommand();
                var table = proxies.ToDataTable();
                adapter.Update(table);

            
        
    

【讨论】:

以上是关于C# Sqlite '约束失败'的主要内容,如果未能解决你的问题,请参考以下文章

唯一约束失败:sqlite 数据库:android

SQLite EF Core - '外键约束失败'

Django 2.0:sqlite IntegrityError:外键约束失败

在 SQLite 中启用外键约束

EF Core / Sqlite 一对多关系在唯一索引约束上失败

插入 android.database.sqlite.SQLiteConstraintException 时出错:NOT NULL 约束失败:result.high(代码 1299)