InsertWithChildrenAsync() 和重复的主键

InsertWithChildrenAsync() and duplicate primary keys

我从网络服务中获得了一个对象列表。数据应存储在 SQLite 数据库中。因此,我可以使用 InsertWithChildrenAsync() 将第一项存储在数据库中,第二项出现异常并且应用程序崩溃。

Class定义:

public class Color
{
    [PrimaryKey]
    public string Code { get; set; }
    public string Name { get; set; }
}
public class Person
{
    [PrimaryKey]
    public int Id { get; set; }
    public string Name { get; set; }
    [ForeignKey(typeof(Color))]
    public string FavoriteColorId { get; set; }
    [OneToOne(CascadeOperations = CascadeOperation.All)]
    public Color FavoriteColor { get; set; }
}

初始化:

var dbPath = DependencyService.Get<IStorageService>().GetFilePathForDB();
DependencyService.Get<IStorageService>().DeleteFile(dbPath);
var db = new SQLiteAsyncConnection(dbPath);
await db.CreateTableAsync<Color>();
await db.CreateTableAsync<Person>();

演示问题的代码:

var pers1 = new Person()
{
    Id = 1,
    Name = "John",
    FavoriteColor = new Color() { Code = "FF0000", Name = "Red" }
};

var pers2 = new Person()
{
    Id = 2,
    Name = "Doe",
    FavoriteColor = new Color() { Code = "FF0000", Name = "Red" }
};

await db.InsertWithChildrenAsync(pers1, true);
await db.InsertWithChildrenAsync(pers2, true);

错误信息

SQLite.SQLiteException: Constraint

如果我使用

就不会发生这种情况
var pers2 = new Person()
{
    Id = 2,
    Name = "Doe",
    FavoriteColor = new Color() { Code = "00FF00", Name = "Green" }
};

问题是同一个主键插入了两次。一种解决方案是使用自动递增,但随后会多次存储相同的数据。如何为不同的对象使用相同的数据?来自 web 服务的数据被解析并随后存储在数据库中。我不会一直将对象保存在内存中。否则我可以使用类似

的东西
Color red = new Color { Code = "00FF00", Name = "Green" };
pers1.FavoriteColor = red;
pers2.FavoriteColor = red;

我需要多对多 table 吗?删除呢?目前,我打算使用 DeleteAsync(),但无法完全删除该条目,因为另一个实例正在使用它。该方法是否考虑到这一点?

我有哪些选择?

您正在两次插入此对象:

new Color() { Code = "00FF00", Name = "Green" }

因此您获得了主键约束错误。我建议您在将该元素插入数据库之前检查该元素是否存在。

如果你有像我这样复杂的数据结构,你可以这样做:主键,不使用自增标志[PrimaryKey, AutoIncrement],必须单独处理。

对于插入,请勿使用 CascadeOperations = CascadeOperation.CascadeInsert。相反,您必须手动插入它们。例如

await StoreColorAsync(db, red);
await db.InsertWithChildrenAsync(pers1, false);

public async Task StoreColorAsync(SQLiteAsyncConnection db, Color color)
{
    if (color == null)
        return;

    var foundItem = await GetColorAsync(db, color.Code);
    if (foundItem != null)
    {
        await db.UpdateAsync(color);
    }
    else
    {
        await db.InsertAsync(color);
    }
}

public async Task<Color> GetColorAsync(SQLiteAsyncConnection db, string colorCode)
{
    var queryResult = await db.Table<Color>().Where(c => c.Code == colorCode).CountAsync();
    if (queryResult > 0)
    {
        return await db.GetAsync<Color>(colorCode);
    }
    else
    {
        return null;
    }
}

对于删除,只对带有自动增量标志的条目使用CascadeOperations = CascadeOperation.CascadeDelete。在这里,我将其他条目留在数据库中,以后可能会重用。在某些特殊事件(例如注销)中,我清除所有表。