Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / WinForms, .Net Framework Новый топик    Ответить
 c#, транзакция в FBDataAdapter.Update()  [new]
aford
Member

Откуда:
Сообщений: 171
Всем привет.
Использую FirebirdSql.Data.FirebirdClient;
На внесение изменений в БД через FBDataAdapter у меня следующий код:

FbTransaction txn = db.BeginTransaction();
da.UpdateCommand.Transaction = txn;
da.InsertCommand.Transaction = txn;
da.DeleteCommand.Transaction = txn;
da.SelectCommand.Transaction = txn; 
da.Update(dt); 
txn.Commit();

Ну в книжке так и написано, что для оборачивания в транзакцию обновления записей в таблице, в случае, если мы генерируем обновляющие запросы автоматом через CommandBuilder, надо перед update() присвоить транзакцию в select адаптера, т.к. он запрашивает метаданные таблицы.
Но у меня выскакивает null reference exception на строке da.UpdateCommand.Transaction = txn, как будто UpdateCommand пустая...
Тогда где и как запускать транзакцию? Я пока учусь, не ругайте)
23 авг 19, 14:42    [21956526]     Ответить | Цитировать Сообщить модератору
 Re: c#, транзакция в FBDataAdapter.Update()  [new]
pation
Member

Откуда: Москва
Сообщений: 4503
aford,

Адаптерами уже никто не пользуется, смотри в сторону орм
а null reference exception, у тебя потому что ты не указал команду
23 авг 19, 18:14    [21956686]     Ответить | Цитировать Сообщить модератору
 Re: c#, транзакция в FBDataAdapter.Update()  [new]
aford
Member

Откуда:
Сообщений: 171
pation, спасибо за совет про орм, обязательно обращу внимание, но сейчас главное в текущем разобраться)
Команду не указывал, она должна была сгенерироваться автоматом при инициализации
var cb = new FbCommandBuilder(da);
MessageBox.Show(cb.GetUpdateCommand.ToString());

Это все используется при открытии таблицы, а код в первом посте отрабатывает по нажатию на кн. сохранить.
26 авг 19, 08:47    [21957466]     Ответить | Цитировать Сообщить модератору
 Re: c#, транзакция в FBDataAdapter.Update()  [new]
aford
Member

Откуда:
Сообщений: 171
Пардон, вот так надо:
MessageBox.Show(cb.GetUpdateCommand().CommandText);

Как я понял, NullReferenceException было из-за того, что FbCommandBuilder был объявлен локально.
Теперь ошибка
+ "транзакция команды не была инициализирована"

System.InvalidOperationException: 'Execute requires the Command object to have a Transaction object when the Connection object assigned to the command is in a pending local transaction. The Transaction property of the Command has not been initialized.'

Хотя задаю вроде правильно
FbTransaction txn = db.BeginTransaction();
MessageBox.Show(cb.GetUpdateCommand().CommandText); // тут ругается
da.UpdateCommand.Transaction = txn;
da.InsertCommand.Transaction = txn;
da.DeleteCommand.Transaction = txn;
da.Update(dt);
txn.Commit();

Если полностью убираю транзакцию
MessageBox.Show(cb.GetUpdateCommand().CommandText);
da.Update(dt);

то все работает и запрос обновления показывает без ошибки.
26 авг 19, 11:30    [21957545]     Ответить | Цитировать Сообщить модератору
 Re: c#, транзакция в FBDataAdapter.Update()  [new]
buser
Member

Откуда: Санкт-Петербург
Сообщений: 4538
aford, эм... и что в мессадж боксе? Есть небольшое ограничение, т.е. вариант когда упдейт команд не будет создан автоматически (не задан "первичный ключ" для дататейбл)...
Возьмите командбилдер - введите свой селект и посмотрите какие команды он вам нагенерит...
P.S.: вы сделайте сперва всё как написано в книжке...
P.P.S.: есть еще вариант c TransactionScope
26 авг 19, 11:36    [21957550]     Ответить | Цитировать Сообщить модератору
 Re: c#, транзакция в FBDataAdapter.Update()  [new]
aford
Member

Откуда:
Сообщений: 171
buser, первичный ключ задан в ст. USER (есть еще 2 ограничения: запрос возвращает данные только из 1 таблицы и первичный ключ есть в резульатах запроса). В мессейджбоксе что-то вроде
автор
UPDATE "TABLE" SET "USER" = @p1, "USER_NAME" = @p2 .. WHERE (("USER" = @p16))

Как раз по первичному ключу USER и строится логика обновления.
buser
вы сделайте сперва всё как написано в книжке...

Я так и делаю :)
Пока не совсем понятно, как мне поможет здесь TransactionScope
26 авг 19, 11:54    [21957566]     Ответить | Цитировать Сообщить модератору
 Re: c#, транзакция в FBDataAdapter.Update()  [new]
buser
Member

Откуда: Санкт-Петербург
Сообщений: 4538
aford, а если показать весь код... от создания конекшна и дата адаптера?
26 авг 19, 12:52    [21957594]     Ответить | Цитировать Сообщить модератору
 Re: c#, транзакция в FBDataAdapter.Update()  [new]
aford
Member

Откуда:
Сообщений: 171
buser,
using System;
using System.Data;
using System.Windows.Forms;
using FirebirdSql.Data.FirebirdClient;

namespace FirebirdConnect
{
    public partial class Form1 : Form
    {        
        public FbConnection db;
        public FbDataAdapter da;
        public DataTable dt;
        public FbCommandBuilder cb;

        private void LoadGrid()
        {
            dt = new DataTable();           
            try
            {                                 
                da = new FbDataAdapter("select * from " + tbTableName.Text, db);                    
                cb = new FbCommandBuilder(da);                                                                                                     
                da.Fill(dt);                                    
                dataGridView1.DataSource = dt;                             
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }            
        }


        private void BtnConnect_Click(object sender, EventArgs e)
        {           
            FbConnectionStringBuilder fbConfig = new FbConnectionStringBuilder()
            {
                Charset = "WIN1251", // используемая кодировка
                UserID = "SYSDBA", // логин
                Password = "123456", // пароль
                Database = "path", // путь к файлу базы данных
                DataSource = "db.domain.ru", // адрес базы
                Port = 3051, // порт
                ServerType = FbServerType.Default 
            };                        
            var conStr = fbConfig.ToString();            
            db = new FbConnection(conStr);            
            db.Open();            
            LoadGrid();
        }

        private void Button1_Click(object sender, EventArgs e)
        {
            LoadGrid();
        }

        private void TsBtnUpdate_Click(object sender, EventArgs e)
        {
            //FbTransaction txn = db.BeginTransaction();                                                                                                                                                                                                                                 
            MessageBox.Show(cb.GetUpdateCommand().CommandText);                                                                                    
            //da.UpdateCommand.Transaction = txn;
            //da.InsertCommand.Transaction = txn;
            // da.DeleteCommand.Transaction = txn;                            
            //da.SelectCommand.Transaction = txn; 
            da.Update(dt); // передаем изменения                            
            //txn.Commit();        
        }
     }
}
26 авг 19, 13:27    [21957617]     Ответить | Цитировать Сообщить модератору
 Re: c#, транзакция в FBDataAdapter.Update()  [new]
Cat2
Member

Откуда: Petroskoi, Karjala
Сообщений: 145742
pation
aford,

Адаптерами уже никто не пользуется, смотри в сторону орм
а null reference exception, у тебя потому что ты не указал команду

Пользуются. ОРМ пользуются те, кто плохо знают возможности баз данных.

aford, открытие транзакций на клиенте - зло.
26 авг 19, 15:04    [21957697]     Ответить | Цитировать Сообщить модератору
 Re: c#, транзакция в FBDataAdapter.Update()  [new]
Cat2
Member

Откуда: Petroskoi, Karjala
Сообщений: 145742
aford
buser,
using System;
using System.Data;
using System.Windows.Forms;
using FirebirdSql.Data.FirebirdClient;

namespace FirebirdConnect
{
    ...
}

Все неправильно. Если используется подход через DataSet, то надо использовать типизированный DataSet
26 авг 19, 15:22    [21957716]     Ответить | Цитировать Сообщить модератору
 Re: c#, транзакция в FBDataAdapter.Update()  [new]
aford
Member

Откуда:
Сообщений: 171
Cat2, я ж DataSet-ом не пользуюсь, у меня одна таблица. Да и типизированный не за чем, если я обращаюсь к разным таблицам. Знаю, что датасеты со строгим контролем типов это хорошо в плане разработки, скорости и т.д., но сейчас меня интересует только почему не цепляется транзакция и вываливается исключение...
автор
открытие транзакций на клиенте - зло

Почему? Где их еще открывать? Если только писать сервер для клиента, который будет работать с БД и управлять с транзакциями?
26 авг 19, 16:38    [21957760]     Ответить | Цитировать Сообщить модератору
 Re: c#, транзакция в FBDataAdapter.Update()  [new]
Cat2
Member

Откуда: Petroskoi, Karjala
Сообщений: 145742
aford
Cat2, я ж DataSet-ом не пользуюсь, у меня одна таблица. Да и типизированный не за чем, если я обращаюсь к разным таблицам. Знаю, что датасеты со строгим контролем типов это хорошо в плане разработки, скорости и т.д., но сейчас меня интересует только почему не цепляется транзакция и вываливается исключение...
автор
открытие транзакций на клиенте - зло

Почему? Где их еще открывать? Если только писать сервер для клиента, который будет работать с БД и управлять с транзакциями?



Если у Вас одна таблица, то Вы не умеете проектировать базы и понятия не имеете о нормализации.
Типизированный Dataset - модель базы данных, удобная для обращения к базе.

Да нафига Вам это цепляние?
Единственный запрос и так выполняется в отдельной транзакции.

Когда нужно в единой транзакции выполнить несколько запросов - используются хранимые процедуры.

Зло в том, что если на клиенте открывается транзакция, то она должна быть и закрыта там же.
Пример. В транзакции два запроса на изменение.
Во время выполнения первого запроса клиент отвалился - террористы взорвали компьютер клиента. СУБД будет ждать "некоторое разумное время", что бы откатить эту операцию.
26 авг 19, 21:49    [21957936]     Ответить | Цитировать Сообщить модератору
 Re: c#, транзакция в FBDataAdapter.Update()  [new]
Barkan
Member

Откуда: Дремучее заМКАДье
Сообщений: 504
aford,

В коде не видно где создаются InsertrCommand, UpdateCommand, DeleteCommand.

Вот для примера мой код (правда это VB.NET + MS SQL, но это не принципиально, главное смотри на транзакцию и команды):

Public NotInheritable Class dbeCORE
   
   Private Sub New()
      'private constructor
   End Sub
   
   Private Shared strSQL As String = ""
   
   ''' <summary>
   ''' Возвращает таблицу стержней для указанного ТП
   ''' </summary>
   ''' <param name="TPR_ID">ИД ТП</param>
   ''' <returns>COR_ID, COR__TPR_ID, COR_NUMBER, COR_COLC, COR__MIX_ID, MIX_NAME, 
   ''' COR_MASSA, MIX_DENSITY, COR__MIX_IDR, MIX_NAMER, MIX_DENSITYR, COR_KIS, COR_GSS</returns>
   Public Shared Function SelectRows(ByVal TPR_ID As Integer) As DataTable
      strSQL = "select C.COR_ID, C.COR__TPR_ID, C.COR_NUMBER, C.COR_COLC, C.COR__MIX_ID, S1.MIX_NAME, " _
             & "C.COR_MASSA, S1.MIX_DENSITY, C.COR__MIX_IDR, S2.MIX_NAME as MIX_NAMER, S2.MIX_DENSITY as MIX_DENSITYR, " _
             & "C.COR_KIS, C.COR_GSS " _
             & "from CORE as C " _
             & "join SANDMIX as S1 on C.COR__MIX_ID=S1.MIX_ID " _
             & "left join SANDMIX as S2 on C.COR__MIX_IDR=S2.MIX_ID " _
             & "where C.COR__TPR_ID=<TPR_ID> order by C.COR_NUMBER"
      strSQL = strSQL.Replace("<TPR_ID>", TPR_ID.ToString())
      Return DBExecutor.ExecuteSelectCommand(strSQL)
   End Function
   
   Public Shared Sub UpdateRows(ByVal DT As DataTable, ByVal TR As SqlTransaction)
      Dim AD As New SqlDataAdapter()
      strSQL = "insert into CORE (COR__TPR_ID, COR__MIX_ID, COR_NUMBER, COR_COLC, COR_MASSA, COR__MIX_IDR, COR_KIS, COR_GSS) " _
             & "values (@TPR_ID, @MIX_ID, @NUMBER, @COLC, @MASSA, @MIX_IDR, @KIS, @GSS)"
      AD.InsertCommand = New SqlCommand(strSQL, DBExecutor.CNN, TR)
      AD.InsertCommand.Parameters.Add("@TPR_ID", SqlDbType.Int, 10, "COR__TPR_ID")
      AD.InsertCommand.Parameters.Add("@MIX_ID", SqlDbType.Int, 10, "COR__MIX_ID")
      AD.InsertCommand.Parameters.Add("@NUMBER", SqlDbType.VarChar, 5, "COR_NUMBER")
      AD.InsertCommand.Parameters.Add("@COLC", SqlDbType.Decimal, 10, "COR_COLC")
      AD.InsertCommand.Parameters.Add("@MASSA", SqlDbType.Decimal, 10, "COR_MASSA")
      AD.InsertCommand.Parameters.Add("@MIX_IDR", SqlDbType.Int, 10, "COR__MIX_IDR")
      AD.InsertCommand.Parameters.Add("@KIS", SqlDbType.Decimal, 10, "COR_KIS")
      AD.InsertCommand.Parameters.Add("@GSS", SqlDbType.TinyInt, 10, "COR_GSS")
      strSQL = "update CORE set COR__TPR_ID=@TPR_ID, COR__MIX_ID=@MIX_ID, COR_NUMBER=@NUMBER, COR_COLC=@COLC, " _
             & "COR_MASSA=@MASSA, COR__MIX_IDR=@MIX_IDR, COR_KIS=@KIS, COR_GSS=@GSS " _
             & "where COR_ID=@COR_ID"
      AD.UpdateCommand = New SqlCommand(strSQL, DBExecutor.CNN, TR)
      AD.UpdateCommand.Parameters.Add("@TPR_ID", SqlDbType.Int, 10, "COR__TPR_ID")
      AD.UpdateCommand.Parameters.Add("@MIX_ID", SqlDbType.Int, 10, "COR__MIX_ID")
      AD.UpdateCommand.Parameters.Add("@NUMBER", SqlDbType.VarChar, 5, "COR_NUMBER")
      AD.UpdateCommand.Parameters.Add("@COLC", SqlDbType.Decimal, 10, "COR_COLC")
      AD.UpdateCommand.Parameters.Add("@MASSA", SqlDbType.Decimal, 10, "COR_MASSA")
      AD.UpdateCommand.Parameters.Add("@COR_ID", SqlDbType.Int, 10, "COR_ID")
      AD.UpdateCommand.Parameters.Add("@MIX_IDR", SqlDbType.Int, 10, "COR__MIX_IDR")
      AD.UpdateCommand.Parameters.Add("@KIS", SqlDbType.Decimal, 10, "COR_KIS")
      AD.UpdateCommand.Parameters.Add("@GSS", SqlDbType.TinyInt, 10, "COR_GSS")
      strSQL = "delete from CORE where COR_ID=@COR_ID"
      AD.DeleteCommand = New SqlCommand(strSQL, DBExecutor.CNN, TR)
      AD.DeleteCommand.Parameters.Add("@COR_ID", SqlDbType.Int, 10, "COR_ID")
      AD.Update(DT)
   End Sub
   
End Class
27 авг 19, 05:34    [21958019]     Ответить | Цитировать Сообщить модератору
 Re: c#, транзакция в FBDataAdapter.Update()  [new]
aford
Member

Откуда:
Сообщений: 171
Cat2
Если у Вас одна таблица, то Вы не умеете проектировать базы и понятия не имеете о нормализации.

Вопрос изначально стоял так "Почему не работает транзакция, почему выскакивает ошибка"
То, что я использую одну таблицу не значит, что я ничего не понимаю в проектировании и т.п., это все сугубо для примера и чтобы разобраться в текущем вопросе. Вот зачем делать такие выводы? Не понимаю.
Cat2
Да нафига Вам это цепляние?

Я ж и говорю, разобраться, никакой цели кроме "Почему так не работает и как надо" я не преследую. Почему в книжке работает, а у меня нет? Компоненты доступа Firebird не работают или я делаю что-то не так? Книга Д. Сеппа, Microsoft ADO.NET. Только что там не firebird, а ole.
+ Стр. 390

string strConn, strSQL;
DataTabLe tbl = new DataTableC);
OleDbConnection en = new OleDbConnection(strConn);
OleDbDataAdapter da = new 01eDbDataAdapter(strSQL, Cn);
OleDbCommandBuilder cb = new QleDbCommandBuilder(da);
cn.Open();
da.Fill(tbl);
OleDbTransaction txn = cn.BeginTransaction();
da.Update(tbl);
txn.Commit();
cn.Close();

При вызове метода DataAdapterL'pdate объект CommandBuilder выбирает из БД
нужные метаданные, используя свойство SelectCommand объекта DataAdapter. Мы
не сопоставили объект Command в свойстве SelectCommand с только что созданной транзакцией. Следовательно, объекту CommandBuilder не удастся воспользоваться этим свойством, и он генерирует исключение.
Если добавить перед вызовом метода Update объекта DataAdapter такую строку, код успешно выполнится:
da.SelectCommand.Transaction = txn;


Cat2
Единственный запрос и так выполняется в отдельной транзакции.

Транзакция в любом случае есть, хоть и не в явном виде.
Cat2
Когда нужно в единой транзакции выполнить несколько запросов - используются хранимые процедуры.

Или пакетные запросы, но вопрос вначале все же был не об этом :)
Cat2
Зло в том, что если на клиенте открывается транзакция, то она должна быть и закрыта там же.
Пример. В транзакции два запроса на изменение.
Во время выполнения первого запроса клиент отвалился - террористы взорвали компьютер клиента. СУБД будет ждать "некоторое разумное время", что бы откатить эту операцию.

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

Barkan, методы обновления создаются при инициализации коммандбилдера, а если еще точнее (по книжке), то в момент обновления он делает селект запрос в таблицу БД и на основании этого запроса генерирует логику обновления.
Без транзакции же все работает и я не прописываю все логику сам.
27 авг 19, 09:05    [21958057]     Ответить | Цитировать Сообщить модератору
 Re: c#, транзакция в FBDataAdapter.Update()  [new]
aford
Member

Откуда:
Сообщений: 171
Разобрался.
Все, что нужно было сделать, это инициализировать новую команду на выборку данных, но уже с транзакцией (ну или сразу задавать транзакцию ей). То есть вместо
FbTransaction txn = db.BeginTransaction();
da.UpdateCommand.Transaction = txn;
da.InsertCommand.Transaction = txn;
da.DeleteCommand.Transaction = txn;
da.SelectCommand.Transaction = txn; 
da.Update(dt); 
txn.Commit();

Надо было сделать так
FbTransaction txn = db.BeginTransaction();								  								
cb.DataAdapter.SelectCommand = new FbCommand("select * from users_test", db, txn);                                                                                                                                         
da.Update(dt);
txn.Commit();
28 авг 19, 10:39    [21958836]     Ответить | Цитировать Сообщить модератору
Между сообщениями интервал более 1 года.
 Re: c#, транзакция в FBDataAdapter.Update()  [new]
dsa0
Member

Откуда:
Сообщений: 3
aford,
Попробовал, как написали, запись не добавляется, ни ошибки, ничего.
Что я сделал не так?
adaptFb.InsertCommand = new FirebirdSql.Data.FirebirdClient.FbCommand("Insert into  NOTICES(NOTICE, FDOC, FCOMMENT, WIN_USER, USER_NAME, DATE_REG) " +
                    "values (@NOTICE,@FDOC,@FCOMMENT,@WIN_USER,@USER_NAME,@DATE_REG", connFB, transFb);
cmdBilderFb = new FbCommandBuilder(adaptFb);
transFb = connFB.BeginTransaction();
adaptFb.InsertCommand.Parameters.Add("@NOTICE", notice);
adaptFb.InsertCommand.Parameters.Add("@FDOC", tbxDoc.Text);
adaptFb.InsertCommand.Parameters.Add("@FCOMMENT", tbxComment.Text);
adaptFb.InsertCommand.Parameters.Add("@WIN_USER", win_USER);
adaptFb.InsertCommand.Parameters.Add("@USER_NAME", userName);
adaptFb.InsertCommand.Parameters.Add("@DATE_REG", dtpCreate.Value.Date);
cmdBilderFb.DataAdapter.SelectCommand = new FirebirdSql.Data.FirebirdClient.FbCommand(strSelect, connFB, transFb);
adaptFb.Update(dTable);
transFb.Commit();
8 апр 21, 09:17    [22305800]     Ответить | Цитировать Сообщить модератору
 Re: c#, транзакция в FBDataAdapter.Update()  [new]
dsa0
Member

Откуда:
Сообщений: 3
А, все, разобрался.
8 апр 21, 14:47    [22305992]     Ответить | Цитировать Сообщить модератору
Все форумы / WinForms, .Net Framework Ответить