Подтвердить что ты не робот

Пакетное обновление/удаление EF5

Каков наилучший способ работы с пакетными обновлениями с использованием (Entity Framework) EF5? У меня есть 2 отдельных случая, в которых я заинтересован:

  • Обновление поля (например, UpdateDate) для списка (списка) между 100 и 100 000 идентификаторов, который является первичным ключом. Вызов каждого обновления по отдельности, похоже, требует больших затрат и занимает много времени.

  • Вставка многих, также между 100 и 100 000, из тех же объектов (например, пользователей) за один раз.

Какой-нибудь хороший совет?

4b9b3361

Ответ 1

  • Есть два проекта с открытым исходным кодом, которые позволяют это: EntityFramework.Extended и расширения платформы Entity Framework. Вы также можете проверить discussion о массовых обновлениях на сайте EF codeplex.
  • Вставка записей 100k через EF - это, в первую очередь, неправильная архитектура приложения. Вы должны выбрать различные легкие технологии для импорта данных. Даже внутренняя работа EF с таким большим набором записей будет стоить вам много времени на обработку. В настоящее время нет решения для пакетных вставок для EF, но есть широкая дискуссия об этой функции на сайте EF code plex.

Ответ 2

Я вижу следующие варианты:

1. Самый простой способ - создать запрос SQL вручную и выполнить через ObjectContext.ExecuteStoreCommand

context.ExecuteStoreCommand("UPDATE TABLE SET FIELD1 = {0} WHERE FIELD2 = {1}", value1, value2);

2. Используйте EntityFramework.Extended

context.Tasks.Update(
    t => t.StatusId == 1, 
    t => new Task {StatusId = 2});

3. Создайте собственное расширение для EF. Существует статья Bulk Delete, где эта цель была достигнута путем наследования класса ObjectContext. Стоит взглянуть. Массовая вставка/обновление может быть реализована таким же образом.

Ответ 3

Возможно, вы не захотите это услышать, но ваш лучший вариант - не использовать EF для массовых операций. Чтобы обновить поле в таблице записей, используйте оператор Update в базе данных (возможно, вызванный через хранимый процесс, преобразованный в функцию EF). Вы также можете использовать метод Context.ExecuteStoreQuery для выпуска инструкции Update в базу данных.

Для массивных вставок лучше всего использовать Bulk Copy или SSIS. EF потребует отдельного попадания в базу данных для каждой вставленной строки.

Ответ 4

Массовые вставки должны выполняться с использованием класса SqlBulkCopy. Пожалуйста, ознакомьтесь с ранее существующим StackOverflow Q & A по интеграции двух: SqlBulkCopy и Entity Framework

SqlBulkCopy намного удобнее, чем bcp (утилита командной строки Bulk Copy) или даже OPEN ROWSET.

Ответ 5

Я согласен с принятым ответом, что ef, вероятно, является неправильной технологией для объемных вставок. Однако, мне кажется, стоит взглянуть на EntityFramework.BulkInsert.

Ответ 6

Вот что я сделал успешно:

private void BulkUpdate()
{
    var oc = ((IObjectContextAdapter)_dbContext).ObjectContext;
    var updateQuery = myIQueryable.ToString(); // This MUST be above the call to get the parameters.
    var updateParams = GetSqlParametersForIQueryable(updateQuery).ToArray();
    var updateSql = [email protected]"UPDATE dbo.myTable
                       SET col1 = x.alias2
                       FROM dbo.myTable
                       JOIN ({updateQuery}) x(alias1, alias2) ON x.alias1 = dbo.myTable.Id";
    oc.ExecuteStoreCommand(updateSql, updateParams);
}

private void BulkInsert()
{
    var oc = ((IObjectContextAdapter)_dbContext).ObjectContext;
    var insertQuery = myIQueryable.ToString(); // This MUST be above the call to get the parameters.
    var insertParams = GetSqlParametersForIQueryable(insertQuery).ToArray();
    var insertSql = [email protected]"INSERT INTO dbo.myTable (col1, col2)
                       SELECT x.alias1, x.alias2
                       FROM ({insertQuery}) x(alias1, alias2)";
    oc.ExecuteStoreCommand(insertSql, insertParams.ToArray());
}    

private static IEnumerable<SqlParameter> GetSqlParametersForIQueryable<T>(IQueryable<T> queryable)
{
    var objectQuery = GetObjectQueryFromIQueryable(queryable);
    return objectQuery.Parameters.Select(x => new SqlParameter(x.Name, x.Value));
}

private static ObjectQuery<T> GetObjectQueryFromIQueryable<T>(IQueryable<T> queryable)
{
    var dbQuery = (DbQuery<T>)queryable;
    var iqProp = dbQuery.GetType().GetProperty("InternalQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
    var iq = iqProp.GetValue(dbQuery, null);
    var oqProp = iq.GetType().GetProperty("ObjectQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
    return (ObjectQuery<T>)oqProp.GetValue(iq, null);
}

Ответ 7

    public static bool BulkDelete(string tableName, string columnName, List<object> val)
    {
        bool ret = true;

        var max = 2000;
        var pages = Math.Ceiling((double)val.Count / max);
        for (int i = 0; i < pages; i++)
        {
            var count = max;
            if (i == pages - 1) { count = val.Count % max; }

            var args = val.GetRange(i * max, count);
            var cond = string.Join("", args.Select((t, index) => $",@p{index}")).Substring(1);
            var sql = $"DELETE FROM {tableName} WHERE {columnName} IN ({cond}) ";

            ret &= Db.ExecuteSqlCommand(sql, args.ToArray()) > 0;
        }

        return ret;
    }