InsertWithChildrenAsync()和重复的主键

时间:2018-05-18 14:09:52

标签: c# sqlite database-design sqlite-net sqlite-net-extensions

我从webservice获取了一个对象列表。数据应存储在SQLite数据库中。因此,我可以使用InsertWithChildrenAsync()将第一项存储在数据库中,第二项我得到一个异常,应用程序崩溃。

班级定义:

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;

我需要一张多对多的桌子吗?删除怎么样?目前,我打算使用DeleteAsync(),但不能完全删除该条目,因为另一个实例正在使用它。该方法是否考虑到了这一点?

我有什么选择?

2 个答案:

答案 0 :(得分:1)

您要插入此对象两次:

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

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

答案 1 :(得分:0)

如果您有像我这样的复杂数据结构,您可以执行以下操作:不使用自动增量标记[PrimaryKey, AutoIncrement]的主键必须单独处理。

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

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。在这里,我将其他条目留在数据库中,以后可能会重复使用。在某些特殊事件(例如注销)中,我清除了所有表格。