我使用OleDbDataAdapter和OleDbCommandBuilder用数据库内容填充DataSet对象,然后根据我在DataSet中所做的更改来更新数据库.问题是我得到了异常:"并发冲突:UpdateCommand影响了预期的1条记录中的0条".我找到了这个错误的解释:
由于创纪录的可能它是从SELECT语句返回后已被修改,但更新之前或DELETE语句发出后,自动生成的UPDATE或DELETE语句包含一个WHERE子句,指定它是否包含所有原始一个行仅更新值并且尚未从数据源中删除.如果自动生成的更新尝试更新已删除的行或不包含DataSet中找到的原始值,则该命令不会影响任何记录,并且会引发DBConcurrencyException.
这意味着自动生成的UPDATE命令会影响数据库中的0行.我使用paradox(db-file)数据库,除了我之外没有人改变它.我猜我的程序在某处改变了同一行两次.我想通过手动执行所有生成的查询来调试我的程序,并找到哪一个不影响任何行(因为实际上我很确定所有更改只进行一次,而bug就在其他地方))).是否可以手动运行自动生成的命令?
我的代码太大而且很复杂,不能在这里发布,但通常它的工作原理是这样的(我做了一个工作项目并从那里开始)
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
PS以前它没有更新并发异常数据库,但一个很大的变化后(我注释掉行"DataAdapter.Update(SPR_KMZ);"很长一段时间进行调试的原因,所以我不知道什么时候这个错误启动投掷)
PSS我的代码中没有INSERT或DELETE,只有UPDATE ...
<< UPDATE >>
我发现问题是什么:如果"DN"字段具有NULL值,那么在更改之后,自动生成的UPDATE语句不会影响任何内容,显然是因为"DN"包含在主键和命令构建器中并没有期望主键字段具有NULL值(谁曾经))),毫不奇怪这个引擎被称为"Paradox")))
这就是为什么
CommandBuilder.GetUpdateCommand().CommandText
Run Code Online (Sandbox Code Playgroud)
在"DN"字段的where子句中有这种模式:
... WHERE ((REZ = ?) AND (DN = ?) AND ...
Run Code Online (Sandbox Code Playgroud)
可空字段的描述如下:
... AND ((? = 1 AND Price IS NULL) OR (Price = ?)) AND ((? = 1 AND Nmed IS NULL) OR (Nmed = ?)) AND ...
Run Code Online (Sandbox Code Playgroud)
PSSS嘿,我可以尝试手动设置UpdateCommand来解决这个问题!)))
以下是我如何手动设置 UpdateCommand,甚至获取正在执行的每个 UPDATE 命令的 SQL 代码!(或多或少))。它在调试时非常有帮助 - 我可以看到在 DataAdapter.Update(DBDataSet) 命令期间未能执行的 sql 查询。
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();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
614 次 |
| 最近记录: |