InsertAll и UpdateAll в sqlite-net-pcl VS InsertOrReplaceAll в SQLite.Net-PCL

Я хочу удалить пакет SQLite.Net-PCL и использовать sqlite-net-pcl, потому что позже я обнаружил, что SQLite.Net-PCL официально не поддерживается .

У меня есть таблицы, в которых GUID хранится как первичный ключ строкового типа в моем проекте Xamarin. У меня есть список записей, поступающих с сервера и в настоящее время использующих метод InserOrReplaceAll для вставки новых записей и обновления существующих, причем все сразу на основе моего GUID первичного ключа.

Теперь у sqlite-net-pcl нет InsertOrReplaceAll метода, но вместо этого есть только InsertAll & UpdateAll методы. Microsoft msdn Link говорит о проверке наличия доступного значения для первичного ключа и на основании этого решите, нужно ли вставлять или обновлять записи.

Но у меня есть случай, когда значение первичного ключа всегда предварительно устанавливается в List перед вставкой или обновлением объекта и я не хочу создавать цикл для проверки, существует ли запись или нет для более чем 500 записей.

Как в этом случае вставить или заменить сразу все свои записи?

Рассмотрим следующий пример, чтобы понять этот сценарий:

using (var conn = new DBConnectionService().GetConnection())
{       
    List<ENTEmployee> employees = new List<ENTEmployee>()
    {
        new ENTEmployee(){ Id = "b977ec04-3bd7-4691-b4eb-ef47ed6796fd", FullName = "AAA BBB", Salary = 15000 },
        new ENTEmployee(){ Id = "c670a3e2-b13f-42b3-849c-fd792ebfd103", FullName = "BBB BBB", Salary = 16000 },
        new ENTEmployee(){ Id = "d961c33c-0244-48dc-8e10-f4f012386eb6", FullName = "CCC BBB", Salary = 17000 },
        new ENTEmployee(){ Id = "35be4508-ff93-4be8-983f-d4908bcc592d", FullName = "DDD BBB", Salary = 18000 },
        new ENTEmployee(){ Id = "0875549c-d06c-4983-b89a-edf81b6aa70d", FullName = "EEE BBB", Salary = 19000 },
    };

    var insertResult = conn.InsertAll(employees);

    //Updated Record
    employees[0].FullName = "AAA Updated";
    employees[0].Salary = 12300;


    //New Records
    employees.Add(new ENTEmployee() { Id = "87f48ecf-715c-4327-9ef3-11712ba4a120", FullName = "FFF BBB", Salary = 20000 });
    employees.Add(new ENTEmployee() { Id = "85f53888-b1e9-460c-8d79-88010f143bcf", FullName = "GGG BBB", Salary = 21000 });

    //Now here, 
    //How to decide which records to be inserted and which records to be updated for List employees?
}

person MilanG    schedule 05.11.2017    source источник
comment
Я запросил проблему / функцию здесь: github.com/praeclarum/sqlite-net/issues / 652   -  person MilanG    schedule 06.11.2017


Ответы (3)


Я реализовал метод расширения, аналогичный их реализации. Вы можете сравнить его с исходным в SQLite.Net-PCL проект.

static public class SQLiteConnectionExtensions
{
    /// <summary>
    ///     Inserts all specified objects.
    ///     For each insertion, if a UNIQUE
    ///     constraint violation occurs with
    ///     some pre-existing object, this function
    ///     deletes the old object.
    /// </summary>
    /// <param name="objects">
    ///     An <see cref="IEnumerable" /> of the objects to insert or replace.
    /// </param>
    /// <returns>
    ///     The total number of rows modified.
    /// </returns>
    static public int InsertOrReplaceAll(this SQLiteConnection connection, IEnumerable objects, bool runInTransaction = true)
    {
        var c = 0;
        if (objects == null)
            return c;

        if (runInTransaction)
        {
            connection.RunInTransaction(() =>
            {
                foreach (var r in objects)
                {
                    c += connection.Insert(r, "OR REPLACE", Orm.GetType(r));
                }
            });
        }
        else
        {
            foreach (var r in objects)
            {
                c += connection.Insert(r, "OR REPLACE", Orm.GetType(r));
            }
        }

        return c;
    }
}
person Genfood    schedule 04.07.2018

Как в этом случае вставить или заменить сразу все свои записи?

Есть два способа решить проблему L:

  1. Просмотрите свой список записей и используйте conn.InsertOrReplace(object) для каждого объекта в вашем списке:

    для (ENTEmployee emp в служащих) {conn.InsertOrReplace (emp); }

  2. Используйте команду Sql для вставки или замены записей (вам нужно создать командную строку):

    string cmd = @"insert or replace into employ(id,FullName,salary) 
                  values('c670a3e2-b13f-42b3-849c-fd792ebfd103', 'fullname1',32),
                        ('d961c33c-0244-48dc-8e10-f4f012386eb6', 'fullname2',23);";
    conn.Execute(cmd);
    
person Elvis Xia - MSFT    schedule 06.11.2017
comment
Решение 1 касается зацикливания всех моих записей, которые я не хочу предпочитать. Рассмотрим случай, когда есть тысячи или лаки записей. Я хочу знать, смогу ли я сделать все сразу без цикла. Решение 2 также не подходит для создания большой строки только для команды. - person MilanG; 06.11.2017
comment
Если вы не предпочитаете эти два решения, то единственное решение, которое я вижу сейчас, - это попросить автора sql-net-pcl добавить функцию обратно. Вы можете открыть проблему на сайте github sql-net-pcl. - person Elvis Xia - MSFT; 06.11.2017
comment
Да, это то, о чем я думал минуту назад. InsertOrReplaceAll был очень хорошим методом в старом SQLite.NET PCL, который делал все за нас в этом случае. - person MilanG; 06.11.2017

Только что протестировал 4 различных способа обновления многих элементов:

Контекст: обновление базы данных продукта (с 7 свойствами в каждом объекте) до 5000 позиций.

  1. использование exists (через countasync на ПК), insertasync и updateasync в цикле foreach: около 30 секунд
  2. с использованием цикла foreach и InsertOrReplaceAsync: около 2 минут
  3. с использованием расширения, предложенного @Genfood: около 92 мс
  4. используя ручной запрос, предложенный @Elvis Xia - MSFT: около 81 мс

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

public static class SqliteExtensions
{
    static async public Task<int> InsertOrReplaceAll(this SQLiteAsyncConnection connection, IEnumerable objects, bool runInTransaction = true)
    {
        var c = 0;
        if (objects == null)
            return c;

        if (runInTransaction)
        {
            await connection.RunInTransactionAsync(nonAsyncConnection =>
            {
                foreach (var r in objects)
                {
                    c += nonAsyncConnection.Insert(r, "OR REPLACE", Orm.GetType(r));
                }
            });
        }
        else
        {
            foreach (var r in objects)
            {
                c += await connection.InsertAsync(r, "OR REPLACE", Orm.GetType(r));
            }
        }

        return c;
    }
}
person Steeve    schedule 15.01.2020