При изучении linq2db столкнулся с непониманием, почему чтение закрывается после первого же Update?
Нельзя одновременно читать и писать? А как это делать в одно транзакции? Или надо материализовать результат чтения?
Вот элементарный код:
using (var db = new TestDB())
{
db.BeginTransaction();
foreach (var st in db.Entinies.Select(_ => _.UID))
{
db.Items.Update(x => false, x => x);
}
db.CommitTransaction();
}
Вот ошибка:
System.InvalidOperationException
HResult=0x80131509
Сообщение = Invalid attempt of read when the reader is closed.
Источник = FirebirdSql.Data.FirebirdClient
Трассировка стека:
at FirebirdSql.Data.FirebirdClient.FbDataReader.CheckState()
at FirebirdSql.Data.FirebirdClient.FbDataReader.Read()
at LinqToDB.Linq.QueryRunner.<ExecuteQuery>d__12`1.MoveNext()
at ConsoleApp11.Program.Main(String[] args) in C:\Users\User\source\repos\ConsoleApp11\ConsoleApp11\Program.cs:line 51
Здравствуйте, Naf2000, Вы писали:
N>При изучении linq2db столкнулся с непониманием, почему чтение закрывается после первого же Update? N>Нельзя одновременно читать и писать? А как это делать в одно транзакции? Или надо материализовать результат чтения?
Может у вас автокоммит включен? Да и само обновление в цикле как-то эммм...
Re[2]: Invalid attempt of read when the reader is closed
Здравствуйте, Слава, Вы писали:
С>Здравствуйте, Naf2000, Вы писали:
N>>При изучении linq2db столкнулся с непониманием, почему чтение закрывается после первого же Update? N>>Нельзя одновременно читать и писать? А как это делать в одно транзакции? Или надо материализовать результат чтения?
С>Может у вас автокоммит включен? Да и само обновление в цикле как-то эммм...
Где управляется? у меня же явно указано начало и фиксации транзакции
Про обновление в цикле — я просто разные сценарии тестирую. Подойдет ли мне фреймворк в целом.
Re: Invalid attempt of read when the reader is closed
Здравствуйте, Naf2000, Вы писали:
N> почему чтение закрывается после первого же Update?
не факт, возможно update в кишках тоже читает т.к. идет обращение к другой таблице (items), падаем именно на итеррации?
и, да, ваш код отнюдь не прост N> Или надо материализовать результат чтения?
думаю да, ToList() в самый раз.
Здравствуйте, Naf2000, Вы писали:
N>При изучении linq2db столкнулся с непониманием, почему чтение закрывается после первого же Update? N>Нельзя одновременно читать и писать? А как это делать в одно транзакции? Или надо материализовать результат чтения?
Надо материализовать через ToList()
Я надеюсь это не рабочий код? Мы ж..у рвем чтобы у вас была возможность работать эффективно с базой, а тут цикл с апдейтами.
Re: Invalid attempt of read when the reader is closed
Здравствуйте, Naf2000, Вы писали:
N>При изучении linq2db столкнулся с непониманием, почему чтение закрывается после первого же Update? N>Нельзя одновременно читать и писать? А как это делать в одно транзакции? Или надо материализовать результат чтения?
Почему закрывается непонятно, но в целом из одного соединения нельзя одновременно и читать данные и DataReader и выполнять другие операции. Надо сперва дочитать до конца чтение (.ToList()), а потом уже выполнять изменения. То же самое будет и при ручной работе в ADO.NET без linq2db.
Re[2]: Invalid attempt of read when the reader is closed
Здравствуйте, Danchik, Вы писали:
D>Здравствуйте, Naf2000, Вы писали:
N>>При изучении linq2db столкнулся с непониманием, почему чтение закрывается после первого же Update? N>>Нельзя одновременно читать и писать? А как это делать в одно транзакции? Или надо материализовать результат чтения?
D>Надо материализовать через ToList()
А зачем ?? Ну то есть — это ж искусственное ограничение, которые linq2db зачем то навязывает клиентам
Re[2]: Invalid attempt of read when the reader is closed
Здравствуйте, fmiracle, Вы писали:
F>Здравствуйте, Naf2000, Вы писали:
N>>При изучении linq2db столкнулся с непониманием, почему чтение закрывается после первого же Update? N>>Нельзя одновременно читать и писать? А как это делать в одно транзакции? Или надо материализовать результат чтения?
F>Почему закрывается непонятно, но в целом из одного соединения нельзя одновременно и читать данные и DataReader и выполнять другие операции. Надо сперва дочитать до конца чтение (.ToList()), а потом уже выполнять изменения. То же самое будет и при ручной работе в ADO.NET без linq2db.
Это какое ограничение MSSQL? Потому что на уровне интерфейса DbConnection ничего такого нет. Ну по крайней мере я ничего такого не вижу.
Re[3]: Invalid attempt of read when the reader is closed
Здравствуйте, Jack128, Вы писали:
F>>Почему закрывается непонятно, но в целом из одного соединения нельзя одновременно и читать данные и DataReader и выполнять другие операции. Надо сперва дочитать до конца чтение (.ToList()), а потом уже выполнять изменения. То же самое будет и при ручной работе в ADO.NET без linq2db. J>Это какое ограничение MSSQL? Потому что на уровне интерфейса DbConnection ничего такого нет. Ну по крайней мере я ничего такого не вижу.
В интерфейсе и не увидишь. В общем случае в теории такого ограничения может и не быть, но на практике в mssql оно есть, оракл и постре, насколько я помню — тоже. Проявляется оно исключением вида "There is already opened datareader" или как-то так оно звучало.
Можно решить включением MultipleActiveResultSets в настройках, но это, емнип, по сути открывает несколько соединения неявным образом.
P.S.
Вспомнив про MultipleActiveResultSets подумал, что может быть и я и не прав и ограничение только на два открытых читателя из БД через одно соединение. Возможно так, да.
Re[3]: Invalid attempt of read when the reader is closed
Здравствуйте, Jack128, Вы писали:
J>Здравствуйте, Danchik, Вы писали:
D>>Здравствуйте, Naf2000, Вы писали:
N>>>При изучении linq2db столкнулся с непониманием, почему чтение закрывается после первого же Update? N>>>Нельзя одновременно читать и писать? А как это делать в одно транзакции? Или надо материализовать результат чтения?
D>>Надо материализовать через ToList() J>А зачем ?? Ну то есть — это ж искусственное ограничение, которые linq2db зачем то навязывает клиентам
Охренеть. Мы держим открытым reader чтобы не материализировать все обьекты сразу, а тут предъявляют претензии.
Re[4]: Invalid attempt of read when the reader is closed
Здравствуйте, Danchik, Вы писали:
D>Здравствуйте, Jack128, Вы писали:
J>>Здравствуйте, Danchik, Вы писали:
D>>>Здравствуйте, Naf2000, Вы писали:
N>>>>При изучении linq2db столкнулся с непониманием, почему чтение закрывается после первого же Update? N>>>>Нельзя одновременно читать и писать? А как это делать в одно транзакции? Или надо материализовать результат чтения?
D>>>Надо материализовать через ToList() J>>А зачем ?? Ну то есть — это ж искусственное ограничение, которые linq2db зачем то навязывает клиентам
D>Охренеть. Мы держим открытым reader чтобы не материализировать все обьекты сразу, а тут предъявляют претензии.
Да нет, претензии в том, что вы закрываете ридер, как только пошел другой запрос через тоже соединение. На голом ADO.NET всё работает как надо
public void SampleSelectTest([IncludeDataSources(false, ProviderName.Firebird)] string context)
{
using (var db = (DataConnection)GetDataContext(context))
{
var connection = db.Connection;
using var transaction = connection.BeginTransaction();
var selectCommand = connection.CreateCommand();
selectCommand.Transaction = transaction;
selectCommand.CommandText = @"
select ""PersonID""
from ""Person""
";
using var reader = selectCommand.ExecuteReader();
while(reader.Read())
{
using var updateCommand = connection.CreateCommand();
updateCommand.Transaction = transaction;
updateCommand.CommandText = string.Format(@"update ""Person"" set ""FirstName"" = 'cfewfwe' where ""PersonID"" = {0}", reader.GetInt32(0));
updateCommand.ExecuteNonQuery();
}
transaction.Rollback();
}
}
Здравствуйте, Naf2000, Вы писали:
N>Здравствуйте, Слава, Вы писали:
С>>Здравствуйте, Naf2000, Вы писали:
N>>>При изучении linq2db столкнулся с непониманием, почему чтение закрывается после первого же Update? N>>>Нельзя одновременно читать и писать? А как это делать в одно транзакции? Или надо материализовать результат чтения?
С>>Может у вас автокоммит включен? Да и само обновление в цикле как-то эммм...
N>Где управляется? у меня же явно указано начало и фиксации транзакции N>Про обновление в цикле — я просто разные сценарии тестирую. Подойдет ли мне фреймворк в целом.
Здравствуйте, Jack128, Вы писали:
J>Да нет, претензии в том, что вы закрываете ридер, как только пошел другой запрос через тоже соединение. На голом ADO.NET всё работает как надо
В этом примере используется два объекта SqlCommand, поэтому и работает. В linq2db DataConnection работает с одной командой. Можно создать два DataConnection, один для чтения, другой для записи и всё должно заработать.
Если нам не помогут, то мы тоже никого не пощадим.
Re: Invalid attempt of read when the reader is closed
Вощем-то уже ответили, почему такое происходит сейчас, но просуммирую в одном сообщении для ясности.
На данный момент в linq2db DataConnection работает с одним инстансом DbCommand.
В данном случае у нас выполняется две SQL комманды — сначала открывается DbDataReader и потом на том же инстансе DbCommand при активном ридере выполняется вторая команда для обновления.
Так как ADO.NET такие сценарии не оговаривает, провайдеры тут ведут себя кто как хочет, но в целом большинство из них не позволяет переиспользовать команду если у неё есть активный ридер. В данном случае например провайдер закрывает ридер когда мы делаем update.
Для этой проблемы я запилил https://github.com/linq2db/linq2db/pull/2728, который такие моменты будет корректно обрабатывать, оставляя в покое инстанс команды с активным ридером, но так как это довольно серьезное изменение, мерж отложен до следующей мажорной версии (4.0).
Вообще мы планируем параллельно выпускать превью 4-ки для таких фич (скорее всего первый пойдет параллельно с 3.3.0 релизом). По сути это будут полностью протестированные релизы, просто в preview статусе, чтобы позволить нам вносить breaking changes
Re[2]: Invalid attempt of read when the reader is closed
Вообще если хочется попробовать заставить это работать, то можно попробовать такое:
using (var db = new TestDB())
{
db.BeginTransaction();
IDbCommand command = null;
foreach (var st in db.Entinies.Select(_ => _.UID))
{
if (command == null)
{
// сохраняем команду используемую с ридером для вызова Dispose после цикла
command = db.Command;
// отсоединяем команду от DataConnection, чтобы он создал новый инстанс для Update
db.Command = null;
}
db.Items.Update(x => false, x => x);
}
// приправить try/finally по вкусуif (command != null) command.Dispose();
db.CommitTransaction();
}
Re[2]: Invalid attempt of read when the reader is closed
Здравствуйте, Danchik, Вы писали:
D>Я надеюсь это не рабочий код? Мы ж..у рвем чтобы у вас была возможность работать эффективно с базой, а тут цикл с апдейтами.
А в чем криминал, разве при коммите не батч отправится? Или каждый Update это доступ к бд?
Кодом людям нужно помогать!
Re[3]: Invalid attempt of read when the reader is closed
Здравствуйте, Sharov, Вы писали:
S>Здравствуйте, Danchik, Вы писали:
D>>Я надеюсь это не рабочий код? Мы ж..у рвем чтобы у вас была возможность работать эффективно с базой, а тут цикл с апдейтами.
S>А в чем криминал, разве при коммите не батч отправится? Или каждый Update это доступ к бд?