2016-06-07 10 views
5

Я использую OleDbDataAdapter и OleDbCommandBuilder, чтобы заполнить объект DataSet содержимым базы данных, а затем обновить базу данных в соответствии с изменениями, внесенными мной в DataSet. Проблема в том, что я получаю исключение: «Нарушение параллелизма: UpdateCommand затронул 0 ожидаемых 1 записей». Я нашел объяснение этой ошибки:Получить команды SQL OleDbCommandBuilder

Because a record could have been modified after it was returned from the SELECT statement, but before the UPDATE or DELETE statement is issued, the automatically generated UPDATE or DELETE statement contains a WHERE clause, specifying that a row is only updated if it contains all original values and has not been deleted from the data source. Where an automatically generated update attempts to update a row that has been deleted or that does not contain the original values found in the DataSet, the command does not affect any records, and a DBConcurrencyException is thrown.

Это означает, что автогенерируемая команда UPDATE влияет 0 строк в базе данных. Я работаю с базой данных парадокса (db-file), и никто не меняет ее, кроме меня. Я предполагаю, что моя программа несколько раз меняет одну и ту же строку. Я хотел отлаживать свою программу, выполняя все сгенерированные запросы вручную и обнаруживая, что какой-то не влияет на какую-либо строку (потому что на самом деле я уверен, что все изменения производятся только один раз, а ошибка где-то еще))). Можно ли запускать автоматически созданные команды вручную?

Мой код является слишком большим и сложным, чтобы разместить его здесь, но в целом она работает, как это (я сделал рабочий проект и взял его оттуда)

using System; 
using System.Data; 
using System.Windows.Forms; 
using System.Data.OleDb; 

namespace OleDBCommandBuilder 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void button1_Click(object sender, EventArgs e) 
     { 
      string cs = @"Provider=Microsoft.Jet.OLEDB.4.0;"; 
      cs += @"Data Source=C:\FOLDER\1\SPR_KMZ\;"; 
      cs += @"Extended Properties=Paradox 5.x;"; 

      OleDbConnection Connection = new OleDbConnection(); 
      Connection.ConnectionString = cs; 

      try 
      { Connection.Open(); } 
      catch (Exception ex) 
      { MessageBox.Show("Error openning database! " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); Environment.Exit(0); } 

      string SQLQuery = "SELECT * FROM SPR_KMZ WHERE REZ<>0"; 
      DataSet SPR_KMZ = new DataSet(); 

      OleDbDataAdapter DataAdapter = new OleDbDataAdapter(); 
      DataAdapter.SelectCommand = new OleDbCommand(SQLQuery, Connection); 
      OleDbCommandBuilder builder = new OleDbCommandBuilder(DataAdapter); 

      try 
      { 
       DataAdapter.Fill(SPR_KMZ); 
      } 
      catch (Exception ex) 
      { 
       System.Windows.Forms.MessageBox.Show(String.Format("Error \n{0}\n{1}", ex.Message, SQLQuery)); 
       Environment.Exit(0); 
      } 

      DataRow[] SPR_KMZ_rows = SPR_KMZ.Tables[0].Select("Fkmz=10000912 AND REZ=1"); 

      foreach (DataRow SPR_KMZ_row in SPR_KMZ_rows) 
      { 
       SPR_KMZ_row["DN"] = Convert.ToDateTime("30.12.1899");//26.12.2008 
       SPR_KMZ_row["Price"] = Convert.ToDouble(0);//168,92 
      } 

      DataAdapter.Update(SPR_KMZ); 

      System.Windows.Forms.MessageBox.Show("Success!"); 
      Environment.Exit(0); 
     } 
    } 
} 

P.S. Раньше он обновлял базу данных без исключения параллелизма, но после большого количества изменений (я прокомментировал строку «DataAdapter.Update (SPR_KMZ)», «долгое время для отладки, поэтому я не знаю, когда именно эта ошибка начиналась с бросок)

PSS нет вставки или удаления в моем коде, только обновления ...

< <UPDATE> >

я нашел то, что была проблема: если поле «DN» имеет значение NULL, то после его изменения, автоматически сгенерированное заявление UPDATE ничего не влияет, очевидно, потому что «DN» содержится в первичном ключе, а построитель команд не ожидал, что для поля первичного ключа будут иметь значения NULL (кто когда-либо будет))), неудивительно, что этот движок называется «Парадокс»)))

вот почему в

CommandBuilder.GetUpdateCommand().CommandText 

в раздел, где для поля "DN" был этот вид узора:

... WHERE ((REZ = ?) AND (DN = ?) AND ... 

в то время как обнуляемых поля описываются следующим образом:

... AND ((? = 1 AND Price IS NULL) OR (Price = ?)) AND ((? = 1 AND Nmed IS NULL) OR (Nmed = ?)) AND ... 

P.S.S.S. Эй, я могу попытаться установить UpdateCommand вручную, чтобы исправить это!))

+0

Есть ли какая-нибудь возможность включить код, который вы используете? –

+0

Поиск отсутствующей инструкции вставки или инструкции удаления. Можно ли уменьшить вашу проблему, уменьшив количество мутаций DataSet? –

ответ

0

Вот как мне удалось установить UpdateCommand вручную и даже получить код SQL для каждой выполняемой команды UPDATE! (Более или менее))). Это очень полезно при отладке - я вижу, что запрос sql не удалось выполнить во время команды DataAdapter.Update (DBDataSet).

public void Update(DataSet DBDataSet) 
{ 
    DataAdapter.RowUpdating += before_update; 
    DataAdapter.Update(DBDataSet); 
} 

public void before_update(object sender, EventArgs e) 
{ 
    //Convert EventArgs to OleDbRowUpdatingEventArgs to be able to use OleDbCommand property 
    System.Data.OleDb.OleDbRowUpdatingEventArgs oledb_e = (System.Data.OleDb.OleDbRowUpdatingEventArgs) e; 

    //Get query template 
    string cmd_txt = oledb_e.Command.CommandText; 

    //Modify query template here to fix it 
    //cmd_txt = cmd_txt.Replace("table_name", "\"table_name\""); 

    //fill tamplate with values 
    string cmd_txt_filled = cmd_txt; 
    foreach(System.Data.OleDb.OleDbParameter par in oledb_e.Command.Parameters) 
    { 
     string par_type = par.DbType.ToString(); 
     string string_to_replace_with = ""; 

     if (par.Value.GetType().Name == "DBNull") 
     { 
      string_to_replace_with = "NULL"; 
     } 
     else 
     { 
      if (par_type == "Int32") 
      { 
       par.Size = 4; 
       string_to_replace_with=Convert.ToInt32(par.Value).ToString(); 
      } 
      else if (par_type == "Double") 
      { 
       par.Size = 8; 
       string_to_replace_with=Convert.ToDouble(par.Value).ToString().Replace(",","."); 
      } 
      else if (par_type == "DateTime") 
      { 
       par.Size = 8; 
       /* In Paradox SQL queries you can't just specify the date as a string, 
       * it will result in incompatible types, you have to count the days 
       * between 30.12.1899 and the required date and specify that number 
       */ 
       string_to_replace_with = DateToParadoxDays(Convert.ToDateTime(par.Value).ToString("dd.MM.yyyy")); 
      } 
      else if (par_type == "String") 
      { 
       string_to_replace_with = '"' + Convert.ToString(par.Value) + '"'; 
      } 
      else 
      { 
       //Break execution if the field has a type that is not handled here 
       System.Diagnostics.Debugger.Break(); 
      } 
     } 

     cmd_txt_filled = ReplaceFirst(cmd_txt_filled, "?", string_to_replace_with); 

    } 

    cmd_txt_filled = cmd_txt_filled.Replace("= NULL", "IS NULL"); 

    //Get query text here to test it in Database Manager 
    //System.Diagnostics.Debug.WriteLine(cmd_txt_filled); 

    //Uncomment this to apply modified query template 
    //oledb_e.Command.CommandText = cmd_txt; 

    //Uncomment this to simply run the prepared update command 
    //oledb_e.Command.CommandText = cmd_txt_filled; 
} 

public string ReplaceFirst(string text, string search, string replace) 
{ 
    int pos = text.IndexOf(search); 
    if (pos < 0) 
    { 
     return text; 
    } 
    return text.Substring(0, pos) + replace + text.Substring(pos + search.Length); 
} 

private static string DateToParadoxDays(string date) 
{ 
    return (Convert.ToDateTime(date) - Convert.ToDateTime("30.12.1899")).TotalDays.ToString(); 
}