2016-06-07 11 views
5

Używam OleDbDataAdapter i OleDbCommandBuilder do wypełniania obiektu DataSet zawartością bazy danych, a następnie aktualizowania bazy danych zgodnie ze zmianami wprowadzonymi w DataSet. Problem polega na tym, że otrzymuję wyjątek: "Naruszenie współbieżności: parametr UpdateCommand wpłynął na 0 oczekiwanych 1 rekordów". Znalazłem wyjaśnienie tego błędu:Uzyskiwanie wygenerowanych poleceń SQL przez 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.

To oznacza, że ​​automatycznie wygenerowane polecenie UPDATE wpłynęło na 0 wierszy w bazie danych. Pracuję z bazą danych paradoks (db-file) i nikt jej nie zmienia oprócz mnie. Myślę, że mój program zmienia gdzieś ten sam wiersz dwa razy. Chciałem debugować mój program, wykonując ręcznie wszystkie wygenerowane zapytania i stwierdzając, który z nich nie ma wpływu na żaden wiersz (ponieważ właściwie jestem pewien, że wszystkie zmiany zostały wprowadzone tylko raz, a błąd jest gdzieś indziej))). Czy można uruchamiać automatycznie generowane polecenia ręcznie?

Mój kod jest zbyt duży i skomplikowany, aby opublikować go tutaj, ale ogólnie działa tak (Zrobiłem projekt roboczy i zabrał go stamtąd)

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); 
     } 
    } 
} 

PS: Poprzednio aktualizował bazę danych bez wyjątku współbieżności, ale po wielu zmianach (przez długi czas komentowałem wiersz "DataAdapter.Update (SPR_KMZ) ;, więc nie wiem, kiedy dokładnie ten błąd zaczął się pojawiać rzut)

PSS nie istnieją żadne wstawia lub usuwa w moim kodu tylko aktualizacje ...

< <UPDATE> >

znalazłem w czym problem: jeśli „DN” pole ma wartość NULL następnie po zmianie go, automatycznie wygenerowana instrukcja UPDATE nie ma wpływu na nic, oczywiście dlatego, że "DN" jest zawarty w kluczu podstawowym, a program budujący polecenia nie oczekiwał, że pole klucza podstawowego będzie miało wartości NULL (kto kiedykolwiek by)), nie zdziwiłoby się tego silnika nazywa się "Paradox")))

dlatego w

CommandBuilder.GetUpdateCommand().CommandText 

w którym klauzula dla "DN" pola nie było tego rodzaju wzoru:

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

podczas gdy pola dopuszczające wartość null są opisane tak:

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

P.S.S.S. Hej, mogę spróbować ustawić UpdateCommand ręcznie, aby to naprawić!)))

+0

Czy istnieje możliwość dołączenia kodu, którego używasz? –

+0

Wyszukaj brakującą instrukcję wstawiania lub instrukcję usuwania. Czy można zmniejszyć swój problem poprzez zmniejszenie liczby mutacji DataSet? –

Odpowiedz

0

Oto jak udało mi się ręcznie ustawić UpdateCommand, a nawet uzyskać kod SQL dla każdego wykonywanego polecenia UPDATE! (Więcej lub mniej)). Jest bardzo pomocny podczas debugowania - widzę, jakie kwerendy sql nie udało się wykonać podczas polecenia 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(); 
} 
Powiązane problemy