代码之家  ›  专栏  ›  技术社区  ›  testing

insertwithchildrenasync()和重复的主键

  •  0
  • testing  · 技术社区  · 6 年前

    我从一个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:约束

    如果我使用

    var pers2 = new Person()
    {
        Id = 2,
        Name = "Doe",
        FavoriteColor = new Color() { Code = "00FF00", Name = "Green" }
    };
    

    问题是同一个主键被插入了两次。一种解决方案是使用自动增量,但是相同的数据会存储多次。如何对不同的对象使用相同的数据?WebService中的数据将被解析,然后存储在数据库中。我不总是把这些东西留在记忆中。否则我可以用

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

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

    我有什么选择?

    2 回复  |  直到 6 年前
        1
  •  1
  •   redent84    6 年前

    您将插入此对象两次:

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

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

        2
  •  0
  •   testing    6 年前

    如果您有像我这样的复杂数据结构,可以执行以下操作:主键,它不使用autoincrement标志 [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 对于带有autoincrement标志的条目。在这里,我将其他条目留在数据库中,稍后可能会重用这些条目。在某些特殊事件(例如注销)中,我清除所有表。