Get OleDbCommandBuilder 's generated SQL commands

谁说我不能喝 提交于 2019-12-10 15:35:30

问题


I use OleDbDataAdapter and OleDbCommandBuilder to fill DataSet object with database contents, and then update database according to a changes that I made in the DataSet. The problem is that I get the exception: "Concurrency violation: the UpdateCommand affected 0 of the expected 1 records". I've found an explanation of this error:

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.

That means that auto generated UPDATE command affected 0 rows in the database. I work with paradox(db-file) database and no one changes it except for me. I guess that my program changes the same row two times somewhere. I wanted to debug my program by executing all generated queries manually and finding which one doesn't affect any row(because actually I'm pretty sure that all changes are made only once and the bug is somewhere else))). Is it possible to run auto generated commands manually?

My code is too big and complicated to post it here but generally it works like this(I made a working project and took it from there)

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. Previously it updated the database without concurrency exception, but after a lot of changes(I commented out the line "DataAdapter.Update(SPR_KMZ);" for a long time for debugging reason, so I don't know when exactly this error started to throw)

P.S.S. there are no INSERTs or DELETEs in my code, only UPDATEs...

<<UPDATE>>

I've found what was the problem: if "DN" field has NULL value then after changing it, the auto-generated UPDATE Statement don't affect anything, obviously because "DN" is contained in a primary key and command builder didn't expect for primary key field to have NULL values(who ever would))), no surprise this engine is called "Paradox")))

that's why in

CommandBuilder.GetUpdateCommand().CommandText

in where clause for "DN" field there was this kind of pattern:

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

while nullable fields are described like this:

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

P.S.S.S. Hey, I can try to set UpdateCommand manually to fix this!)))


回答1:


Here is how I've managed to set the UpdateCommand manually and even get SQL code for every UPDATE command that is being executed!(more or less)). It is very helpful while debugging - I can see what sql query failed to execute during DataAdapter.Update(DBDataSet) command.

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


来源:https://stackoverflow.com/questions/37670358/get-oledbcommandbuilder-s-generated-sql-commands

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!