2016-06-07 11 views
5

Uso OleDbDataAdapter e OleDbCommandBuilder per riempire l'oggetto DataSet con il contenuto del database e quindi aggiornare il database in base alle modifiche apportate al DataSet. Il problema è che ottengo l'eccezione: "Violazione della concorrenza: il UpdateCommand ha influenzato 0 dei 1 record previsti". Ho trovato una spiegazione di questo errore:Ottiene i comandi SQL generati da 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.

Ciò significa che automaticamente generato comando UPDATE influenzato 0 righe nel database. Lavoro con il database paradosso (db-file) e nessuno lo cambia tranne me. Immagino che il mio programma cambi la stessa riga due volte da qualche parte. Volevo eseguire il debug del mio programma eseguendo manualmente tutte le query generate e trovando quale non influenza alcuna riga (perché in realtà sono abbastanza sicuro che tutte le modifiche vengano apportate una sola volta e che il bug sia da qualche altra parte))). È possibile eseguire manualmente i comandi generati automaticamente?

Il mio codice è troppo grande e complicato per postare qui ma in genere funziona così (ho fatto un progetto di lavoro e ha preso da lì)

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. Precedentemente ha aggiornato il database senza eccezione di concorrenza, ma dopo molte modifiche (ho commentato la riga "DataAdapter.Update (SPR_KMZ);" per un lungo periodo di tempo per il debug del motivo, quindi non so quando è iniziato esattamente questo errore lancio)

PSS non ci sono inserimenti o eliminazioni nel mio codice, solo gli aggiornamenti ...

< <UPDATE> >

che ho trovato quello che era il problema: se il campo "DN" ha un valore NULL poi dopo averlo cambiato, l'istruzione UPDATE generata automaticamente non influisce su nulla, ovviamente perché "DN" è contenuto in una chiave primaria e il builder del comando non si aspettava che il campo della chiave primaria avesse valori NULL (chi avrebbe mai voluto)), non sorprende questo motore si chiama "Paradox")))

ecco perché in

CommandBuilder.GetUpdateCommand().CommandText 

in cui la clausola per il campo "DN" c'era questo tipo di modello:

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

mentre i campi Null vengono descritti in questo modo:

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

P.S.S.S. Ehi, posso provare a impostare UpdateCommand manualmente per risolvere il problema!))

+0

C'è qualche possibilità di includere il codice che si sta utilizzando? –

+0

Ricerca di un'istruzione di inserimento mancante o di un'istruzione di eliminazione. È possibile ridurre il problema riducendo la quantità di mutazioni DataSet? –

risposta

0

Ecco come sono riuscito a impostare manualmente UpdateCommand e persino ottenere codice SQL per ogni comando UPDATE che si sta eseguendo! (Più o meno)). È molto utile durante il debug - Posso vedere quale query sql non è riuscita ad eseguire durante il comando 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(); 
} 
Problemi correlati