Форум программистов, компьютерный форум, киберфорум
C#: Базы данных
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.76/29: Рейтинг темы: голосов - 29, средняя оценка - 4.76
 Аватар для spektr_011
15 / 5 / 1
Регистрация: 28.11.2013
Сообщений: 56
MS SQL

Грамотно добавлять, удалять, обновлять при foreach

31.07.2018, 11:43. Показов 6486. Ответов 44

Студворк — интернет-сервис помощи студентам
Добрый день.

Приложение раз в две минуты синхронизирует данные с сервера. Если есть изменения, получает коллекции с итемами по типу изменения updatedItems, createdItems и deleteditems.

Затем прохожусь по каждой коллекции и внутри цикла в зависимости от типа ChangeType
добавляю:
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
List<ItemClass> created_ivents = new List<ItemClass>();
 
                            foreach (newitem in createdItems)
                            {
                                ItemClass item1 = new ItemClass
                                {
                                    Value1 = newitem.Value1,
                                    Value2 = newitem.Value2,
                                    Value3= newitem.Value3
                                };
                                created_ivents.Add(item1);
                            }
 
                            using (DataContext db = new DataContext(connectionString))
                            {
                                // добавляем его в таблицу
                                db.GetTable<ItemClass>().InsertAllOnSubmit(created_ivents);
                                db.SubmitChanges();
                            }
                            created_ivents.Clear();

обновляю:
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
List<ItemClass> updated_ivents = new List<ItemClass>();
                            foreach (newitem in updatedItems)
                            {
                                using (DataContext db = new DataContext(connectionString))
                                {
                                    ItemClass uptditem = db.GetTable<ItemClass>().Where(u => u.UniqueId == newitem.Id).First();
 
                                    uptditem.Value1 = newitem.Value1;
                                    uptditem.Value2 = newitem.Value2;
                                    uptditem.Value3 = newitem .Value3;
 
                                    db.SubmitChanges();
                                }
                            }

и удаляю:
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
foreach (olditem in deleteditems)
                            {
                                using (SqlConnection connection = new SqlConnection(connectionString))
                                {
                                    connection.Open();
                                    string sqlExpression2 = "DELETE FROM Table1 WHERE UniqueId = @UniqueId";
 
                                    SqlCommand com = new SqlCommand(sqlExpression2, connection)
                                    {
                                        CommandType = System.Data.CommandType.Text
                                    };
 
                                    com.Parameters.AddWithValue("@UniqueId", olditem.ItemId);
                                    com.CommandText = sqlExpression2;
                                    int result = com.ExecuteNonQuery();
                                    //Console.WriteLine("Удалено {0}", result);
                                }
                            }


Грубо говоря, при создании foreach загоняет все в лист и 1 раз InsertAllOnSubmit(). При удалении каждый раз внутри foreach sql-запрос на удаление по id (т.е. если 10 или 20 удаленных записей, то будет 10 или 20 запросов). При обновлении каждый раз внутри foreach получаю элемент, обновляю все значения и SubmitChanges(), при этом на сколько я понял linq кэширует, сверяет запись и затем обновляет, т.е. вон сколько всего.

Вопрос: как сделать грамотно? Или и так сойдет?)
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
31.07.2018, 11:43
Ответы с готовыми решениями:

Как добавлять и удалять записи из БД и будет ли это приложение (будильник) работать на других ПК
Доброго времени суток! Помогите новичку разобраться. Стоит задача: написать напоминалку (будильник), которая весь день в определенное время...

Как в datagrid добавлять и удалять колонки
Как можно сделать динамическое добавление и удаление колонок в datagrid? В devexpress есть реализация, добавления, удаления колонок, а в...

Как удалять и добавлять вертексы в шейдер?
Всё копаюсь с морфингом. Есть необходмость в определённые момент добавлять некоторые НОВЫЕ грани в шейдер и удалять из - него другие. Так...

44
 Аватар для pincet
1655 / 1154 / 173
Регистрация: 23.07.2010
Сообщений: 6,914
01.08.2018, 16:19
Студворк — интернет-сервис помощи студентам
merge не предлагать?
2
Эксперт .NET
 Аватар для Usaga
14815 / 9589 / 1368
Регистрация: 21.01.2016
Сообщений: 36,114
01.08.2018, 16:57
Всё время забываю про эту замечательную команду.
0
 Аватар для spektr_011
15 / 5 / 1
Регистрация: 28.11.2013
Сообщений: 56
01.08.2018, 18:38  [ТС]
Цитата Сообщение от pincet Посмотреть сообщение
merge не предлагать?
это из одного скула в другой? верно я понял? По-диагонали глянул примеры, не понимаю как, если таблицы в разных бд на разных серваках. Но это уже совсем другая история) Примем за начальные условия, что доступа к бд источника нет. Но спасибо, не знал о таком. Все-таки раз пьянка зашла надо добить.

Тем времинем вроде пересел на EF.
Кликните здесь для просмотра всего текста
удаляю:
C#
1
2
3
4
5
6
7
8
9
10
foreach (item in deleteditems)
                           {
                                using (userdbEntities db = new userdbEntities())
                                {
                                    var item4del = db.Table.Where(m => m.UniqueId == item.id);
                                    db.Table.RemoveRange(item4del);
                                    db.SaveChanges();
                                }      
                            }
}
добавляю:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
List<ItemClass> created_ivents = new List<ItemClass>();
 
                            foreach (newitem in createdItems)
                            {
                                ItemClass item1 = new ItemClass
                                {
                                    Value1 = newitem.Value1,
                                    Value2 = newitem.Value2,
                                    Value3= newitem.Value3
                                };
                                created_ivents.Add(item1);
                            }
 using (userdbEntities db = new userdbEntities())
                            {
                                db.Table.AddRange(created_ivents);
                                db.SaveChanges();
                            }
                            created_ivents.Clear();
и обновляю:
C#
1
2
3
4
5
6
7
8
9
10
11
12
List<ItemClass> updated_ivents = new List<ItemClass>();
                            foreach (newitem in updatedItems)
                            {
                                using (userdbEntities db = new userdbEntities())
                                {
                                    var olditem = db.Table.Where(o => o.UniqueId == newitem.Id).First();
                                    olditem.Value1 = newitem.Value1;
                                    olditem.Value2 = newitem.Value2;
                                    olditem.Value3 = newitem.Value3;
                                    db.SaveChanges();
                                }
                            }


Т.е. удаляю и обновляю я по-прежнему каждый раз по 1, и добавляю как и раньше весь список скопом, но теперь с помощью великого AddRange
Позже надо попробовать как-то из списка deleteditems собрать их айдишники в List<int> IDs и попробовать что-то типа
C#
1
2
db.Table.RemoveRange(db.Table.Where(d => IDs.Contains(d.UniqueId)));
db.SaveChanges();
т.е. чтобы RemoveRange удалил одним запросом. AddRange и RemoveRange нормальны в плане правильности?
А вот с обновлением не понятно что делать. Не знаю как батч-запросом. Что в linq, что в EF каждый раз нужно трогать значения объекта и сохраняться.
0
Эксперт .NET
 Аватар для Usaga
14815 / 9589 / 1368
Регистрация: 21.01.2016
Сообщений: 36,114
02.08.2018, 06:52
Лучший ответ Сообщение было отмечено spektr_011 как решение

Решение

spektr_011, так было бы правильнее:

удаление:
C#
1
2
3
4
5
6
7
var itemsToDelete = deleteditems.Select(x => new ItemClass{ UniqueId == x.id} ).ToArray();
 
using (userdbEntities db = new userdbEntities())
{
    db.Table.RemoveRange(itemsToDelete);
    db.SaveChanges();
}
обновление:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var itemsToUpdate = new List<ItemClass>();
using (userdbEntities db = new userdbEntities())
{
    foreach (newitem in updatedItems)
    {
        var olditem = db.Table.AsNoTracking().FirstOrDefault(newitem.Id);
        if (olditem != null)
        {
            olditem.Value1 = newitem.Value1;
            olditem.Value2 = newitem.Value2;
            olditem.Value3 = newitem.Value3;
            itemsToUpdate.Add(itemsToUpdate);
        }
    }
    foreach (item in itemsToUpdate)
    {
        db.Entry(item).State = EntityState.Modified;
    }
    db.SaveChanges();
}
Цитата Сообщение от spektr_011 Посмотреть сообщение
Не знаю как батч-запросом.
ПРосто не надо сохранять на каждой итерации. EF не такой тупой, он найдёт все изменения в контексте и пошлёт в базу за одно обращение.
1
 Аватар для spektr_011
15 / 5 / 1
Регистрация: 28.11.2013
Сообщений: 56
02.08.2018, 11:18  [ТС]
Цитата Сообщение от Usaga Посмотреть сообщение
так было бы правильнее:
щас опробую.
Цитата Сообщение от Usaga Посмотреть сообщение
EF не такой тупой, он найдёт все изменения в контексте и пошлёт в базу за одно обращение.
так как вернее будет, сохранять 3 раза (после обновить, удалить, добавить) или вообще 1 раз в конце?
0
Эксперт .NET
 Аватар для Usaga
14815 / 9589 / 1368
Регистрация: 21.01.2016
Сообщений: 36,114
02.08.2018, 11:32
spektr_011, можно и один раз всё сделать. EF сам всё обновит, добавит и обновит. Только если записей много (суммарно), то контекст раздуется и процесс затянется.

В три подхода данные вносить - нормально.
0
 Аватар для spektr_011
15 / 5 / 1
Регистрация: 28.11.2013
Сообщений: 56
02.08.2018, 14:15  [ТС]
Usaga,
Цитата Сообщение от Usaga Посмотреть сообщение
var itemsToDelete = deleteditems.Select(x => new ItemClass{ UniqueId == x.id} ).ToArray();
на сколько я понимаю itemsToDelete должны быть сущности из db.Table, подлежащие удалению, а не из deleteditems. deleteditems это и так IEnumerable<ItemClass> с удаленными в источнике итемами. У них классы то разные (если я правильно выразился). В общем как-то эта строка не работает. Как выделить сущности из таблицы, чьи id совпадают с id объектов из deleteditems?
0
Эксперт .NET
 Аватар для Usaga
14815 / 9589 / 1368
Регистрация: 21.01.2016
Сообщений: 36,114
02.08.2018, 17:38
spektr_011, что именно не работает?

Вообще, данный пример показывает главную идея: чтобы удалить запись её не обязательно предварительно из базы вытаскивать, достаточно создать класс того же типа и назначить ему ID записи, что нужно удалить.

Добавлено через 1 минуту
Если не компилируется из-за двойного равно (==), то это ошибка. Там должно быть присваивание)
0
 Аватар для spektr_011
15 / 5 / 1
Регистрация: 28.11.2013
Сообщений: 56
02.08.2018, 18:03  [ТС]
Цитата Сообщение от Usaga Посмотреть сообщение
что именно не работает?
"Ссылка на объект не указывает на экземпляр объекта." Это все внутри if (deleteditems.Any()) {}, так что deleteditems не null.
0
Эксперт .NET
 Аватар для Usaga
14815 / 9589 / 1368
Регистрация: 21.01.2016
Сообщений: 36,114
02.08.2018, 18:12
Цитата Сообщение от spektr_011 Посмотреть сообщение
Это все внутри if (deleteditems.Any()) {}, так что deleteditems не null.
Эм. По-вашему, Any() проверяет ссылку на коллекцию на равенство Null?
0
 Аватар для spektr_011
15 / 5 / 1
Регистрация: 28.11.2013
Сообщений: 56
02.08.2018, 18:34  [ТС]
Usaga, не так выразился, проверяет что в коллеции что-либо есть, так же?
0
Эксперт .NET
 Аватар для Usaga
14815 / 9589 / 1368
Регистрация: 21.01.2016
Сообщений: 36,114
02.08.2018, 18:45
Да, но если сама ссылка никуда не указывает (null), то такую ошибку и получите. И ещё: вы на какой строке это исключение-то ловите?)
0
 Аватар для spektr_011
15 / 5 / 1
Регистрация: 28.11.2013
Сообщений: 56
02.08.2018, 18:51  [ТС]
Цитата Сообщение от Usaga Посмотреть сообщение
обновление:
обновление вроде работет. Немного не понимаю как: получаем сущность по id и говорим не кэшировать, трогаем значения, суем в список, говорим что все в списке измененные, сохраняем и вуаля! В чем отличие в случае, когда проходимся циклом по updateditems, каждый раз получаем по id, трогаем и после цикла сохраняем?

зы: с удалением по прежнему запара.

Добавлено через 4 минуты
Цитата Сообщение от Usaga Посмотреть сообщение
вы на какой строке это исключение-то ловите?)
именно на этой
C#
1
var items4delete = deletedItems.Select(s => new ItemClass{ UniqueId = s.Id.UniqueId}).ToArray();
0
Эксперт .NET
 Аватар для Usaga
14815 / 9589 / 1368
Регистрация: 21.01.2016
Сообщений: 36,114
02.08.2018, 18:55
Разница в том, что при выгрузке данных EF сначала ищет объект в контексте и только потом материализует (и ложит в контекст, если нет AsNoTracking). Чем больше объектов в контексте, тем дольше каждая последующая выборка.

Так же и при SaveChanges - происходит перебор всех объектов в кеше и определение какие из них изменились (и что в них изменилось).

Гораздо эффективнее сделать это один раз после того, как все данные были получены и изменены.

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

Записи в кеш добавятся сразу, за раз с одновременным указанием того, что они изменены (State = Modified). Когда будет вызов SaveChanges() EF увидит состояние Modified и не полезет в сущность смотреть, что именно было изменено.

Т.е. такой вариант будет заметно эффективнее.

Добавлено через 1 минуту
Цитата Сообщение от spektr_011 Посмотреть сообщение
именно на этой
Ну так посмотрите в отладчике, что именно у вас имеет значение null. Вариантов два: или deletedItems или s.Id у одной из записей.
1
 Аватар для spektr_011
15 / 5 / 1
Регистрация: 28.11.2013
Сообщений: 56
02.08.2018, 19:25  [ТС]
Цитата Сообщение от Usaga Посмотреть сообщение
EF увидит состояние Modified и не полезет в сущность смотреть, что именно было изменено
отличное решение, согласен)

Добавлено через 29 минут
Цитата Сообщение от Usaga Посмотреть сообщение
Ну так посмотрите в отладчике,
да, там когда из общей кучи изменений разбрасывал в коллекции по типу, удаленные перегонял в другой класс, хотя он таквым не является. В общем исправил deleteditems, items4delete не нулевой, все значения пусты, кроме ID, он совпадает с объектом в базе все ок.
Но теперь исключение на строчке
C#
1
db.Table.RemoveRange(items4delete);
"Не удалось удалить объект, поскольку он не найден в ObjectStateManager."
Символично номер строки 404)
0
Эксперт .NET
 Аватар для Usaga
14815 / 9589 / 1368
Регистрация: 21.01.2016
Сообщений: 36,114
02.08.2018, 19:34
Цитата Сообщение от spektr_011 Посмотреть сообщение
"Не удалось удалить объект, поскольку он не найден в ObjectStateManager."
Нужно приаттачить объекты к контексту:

C#
1
db.Table.Arrach(каждый объект из коллекции);
Или сразу выставить State на удалённый (как с обновлением), тогда SaveChanges() поудаляет такие записи.
0
 Аватар для spektr_011
15 / 5 / 1
Регистрация: 28.11.2013
Сообщений: 56
02.08.2018, 19:52  [ТС]
Usaga,
Не удалось присоединить сущность типа "AppName.ItemClass", поскольку другая сущность этого же типа уже имеет такое же значение первичного ключа. Такое возможно при использовании метода "Attach" или задании сущности состояния "Unchanged" или "Modified", если какие-либо сущности в графе имеют конфликтующие значения ключей. Такое возможно из-за того, что некоторые сущности являются новыми и пока еще не получили значения ключей, сформированные базой данных. В этом случае используйте метод "Add" или состояние сущности "Added" для отслеживания графа, а затем задайте сущностям, которые не являются новыми, состояние "Unchanged" или "Modified".
вот собака) хотя в базе нет строки с первичным ключом 0.
0
Эксперт .NET
 Аватар для Usaga
14815 / 9589 / 1368
Регистрация: 21.01.2016
Сообщений: 36,114
02.08.2018, 20:05
spektr_011, естественно, что в контексте не должно быть сущностей одного типа с идентичными значениями первичных ключей.
0
 Аватар для spektr_011
15 / 5 / 1
Регистрация: 28.11.2013
Сообщений: 56
03.08.2018, 11:56  [ТС]
Usaga,
теперь новое исключение:
Инструкция update, insert или delete затронула непредвиденное число строк (0) в хранилище. Сущности могли быть изменены или удалены с момента их загрузки.
Т.е. не совоставляется запись без праймари ки?
Немного поигрался, если при создании
C#
1
var items4delete = deletedItems.Select(s => new ItemClass{ UniqueId = s.Id.UniqueId}).ToArray();
в лоб присвоить ключ, идентичный ключу в таблице в бд, удаляет. Но он же никак не известен. Что в такой ситуации делать?
0
Эксперт .NET
 Аватар для Usaga
14815 / 9589 / 1368
Регистрация: 21.01.2016
Сообщений: 36,114
03.08.2018, 12:01
Цитата Сообщение от spektr_011 Посмотреть сообщение
в лоб присвоить ключ, идентичный ключу в таблице в бд, удаляет. Но он же никак не известен. Что в такой ситуации делать?
Что? Кто неизвестен? ID записи? Тогда что и как вы собрались удалять?
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
03.08.2018, 12:01

Как добавлять и удалять элементы из очереди?
Не нашел методы Push и Pop для очереди.

Возможность добавлять и удалять поля в модуле
Уважаемые специалисты, помогите разобраться: Нужно реализовать в модуле возможность добавления и удаления полей из админки. Примерно так:...

Объясните как удалять, добавлять и сортировать строки
подскажите, пожалуйста,как удалять, добавлять и сортировать строки? мне казалось,что через оператор delete, а оказывается как то по...

Как правильнее добавлять и удалять заголовки сообщений?
Как правильнее добавлять и удалять заголовки сообщений: делать экземпляр HttpRequestMessage или добавлять и удалять непосредственно в...

Как динамически добавлять/удалять страницы ViewPager
Добрый день Всем! как можно добавлять и удалять страницы во ViewPager? пытаюсь делать так: MainActiv: ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Особенности gif сгенерированных в Си
russiannick 21.10.2026
Рассмотрим генерацию gif файлов, которые в Си имеют свои особенности. Gif представляет собой графический формат популярный в интернете. Посмотрим на палитру. Gif файл сгенерированный в си отличается. . .
FPS контроллер с анимированными Y-Bot и 3D-звуком miniaudio (Box3D, Recast, BehaviorTree.CPP, ozz-animation, ImGui, OpenGL и SDL3)
8Observer8 18.10.2026
Содержание блога | English Version Полное мультиплатформенное руководство по стартовому проекту с FPS-контроллером игрока, анимированными роботами Y-Bot с 4-слойным блендингом, 3D-физикой Box3D,. . .
Локальный http сервер
Argus19 17.10.2026
Локальный http сервер На работе, на двух локальных компьютерах круглосуточно работают программы опроса приборов с записью в книги Excel,которые раз в сутки записываются на сервер компании. Т. к. . . .
Копировать Windows на новый диск на UEFI
damix 17.10.2026
https:/ / uriesk. wordpress. com/ 2016/ 11/ 01/ move-window-10-to-ssd-on-a-uefi-dualboot-system-with-basic-linux-tools/ TL DR Вставляем диск, а старый подключаем через переходник, грузимся с лайва. . .
ИИ не может найти нужный язык в списке
Supersumestria 05.10.2026
Я ему даю вот такое изображение и прошу найти и подчеркнуть немецкий язык. Возвращает он вот это: https:/ / i. **********/ vqBWLe2. png Нужную строчку в 3й колонке просто выдумал. . Это. . .
Новая последняя моя музыка в SUNO
zorxor 05.10.2026
Здравствуйте, дорогие мои друзья! С большой радостью я хотел бы представить вам свою новую последнею музыку, которую сгенерировала мне по моей просьбе нейросеть SUNO. С уважением, zorxor. Это. . .
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js. В помощники взял Яндекс-Алису. Было создано три зала на разные интересы. исторические и ретро сериал Хичкок. . .
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru