InsertWithChildrenAsync () и дубликаты первичных ключей - PullRequest
0 голосов
/ 18 мая 2018

Я получаю список объектов из веб-сервиса.Данные должны храниться в базе данных 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" }
};

Проблема в том, что один и тот же первичный ключ вставляется дважды.Одним из решений будет использование автоматического увеличения, но затем одни и те же данные сохраняются несколько раз.Как я могу использовать одни и те же данные для разных объектов?Данные из веб-сервиса анализируются и впоследствии сохраняются в базе данных.Я не держу объекты в памяти все время.В противном случае я мог бы использовать что-то вроде

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

Нужен ли мне стол «многие ко многим»?Как насчет удаления?В настоящее время я планирую использовать DeleteAsync(), но запись не может быть удалена полностью, поскольку ее использует другой экземпляр.Метод учитывает это?

Какие у меня варианты?

Ответы [ 2 ]

0 голосов
/ 22 мая 2018

Если у вас сложная структура данных, как у меня, вы можете сделать следующее: Первичные ключи, которые не используют флаг автоинкремента [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 для записей с флагом автоинкремента.Здесь я оставляю другие записи в базе данных, которые, возможно, будут использованы позже.При некоторых особых событиях (например, при выходе из системы) я очищаю все таблицы.

0 голосов
/ 21 мая 2018

Вы вставляете этот объект дважды:

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

Поэтому вы получаете ошибку ограничения первичного ключа.Я бы порекомендовал вам проверить наличие этого элемента, прежде чем вставлять его в базу данных.

...