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
。在这里,我将其他条目留在数据库中,以后可能会重用。在某些特殊事件(例如注销)中,我清除所有表。
我从网络服务中获得了一个对象列表。数据应存储在 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
。在这里,我将其他条目留在数据库中,以后可能会重用。在某些特殊事件(例如注销)中,我清除所有表。