请帮助解决我的问题。尝试从我用 C# 编写的应用程序更新 MS Access(2000 格式)数据库 (.mdb) 中新添加的表记录时,出现错误“并发冲突:UpdateCommand 影响了预期的 1 条记录中的 0 条”。这个错误相当普遍,我尝试了不同论坛上建议的解决方案,但没有成功。
这是我一步一步做的:
我在 mdb 中有一个表“TRACKS”,其中包含以下列:
- ID - 类型“自动编号”(键列)
- 标题 - '文本'
- 全文 - '文本'
长度 - '日期/时间'
我以这种方式建立与数据库的连接并获取表记录:
public partial class MainForm : Form { public OleDbConnection dbConn = new OleDbConnection(); public DataSet dataset = new DataSet(); protected OleDbDataAdapter adTracks = new OleDbDataAdapter(); protected OleDbCommandBuilder cmb; ArrayList arrArtists = new ArrayList(); public MainForm(string strFileName) { InitializeComponent(); cmb = new OleDbCommandBuilder(adTracks); } private void OnLoad(object sender, EventArgs e) { dbConn.ConnectionString = Properties.Settings.Default.dbConnectionString; OleDbCommand cmTracks = new OleDbCommand("Select * from Tracks", dbConn); OleDbDataAdapter adapter = new OleDbDataAdapter(); try { dbConn.Open(); adTracks.SelectCommand = cmTracks; adTracks.Fill(dataset, "Tracks"); } catch (Exception err) { MessageBox.Show(err.Message); return; } finally { dbConn.Close(); } cboOriginal.DataSource = dataset.Tables["Tracks"]; cboOriginal.DisplayMember = "FullTitle"; cboOriginal.ValueMember = "ID"; cboOriginal.SelectedIndex = -1; adTracks.RowUpdated += new OleDbRowUpdatedEventHandler(OnRowUpdated); } }
然后我使用此代码向表中添加一条新记录(
txtTitle
并且txtGenTitle
控件包含记录的值):DataTable dt; DataRow dr; int newID; dt = dataset.Tables["Tracks"]; dr = dt.NewRow(); dr["Title"] = txtTitle.Text; dr["FullTitle"] = txtGenTitle.Text; dt.Rows.Add(dr); try { dbConn.Open(); adTracks.Update(dt); } catch (Exception err) { MessageBox.Show("Error adding new track '" + txtGenTitle.Text + "':\n" + err.Message); return; } finally { dbConn.Close(); } res = dt.Select("FullTitle = '" + txtGenTitle.Text.Replace("'", "''") + "'"); if (res.Length != 0) { newID = (int)res[0]["ID"]; // continue with newID }
此代码成功执行:新记录添加到表中,本地 DataTable 和 mdb 文件中的实际表。在此处理程序中接收到键列的新自动递增值:
protected void OnRowUpdated(object sender, OleDbRowUpdatedEventArgs args) { if (args.StatementType == StatementType.Insert) { OleDbCommand idCMD = new OleDbCommand("SELECT @@IDENTITY", dbConn); args.Row["ID"] = (int)(idCMD.ExecuteScalar()); } }
具有此 ID 的行现在有
RowState == Unchanged
,所以一切似乎都正常。现在我想更新这个新添加的记录中的一些值(来自
txtLength
控制):DataTable dt; DataRow dr; DataRow[] res; dt = dataset.Tables["Tracks"]; res = dt.Select("FullTitle = '" + txtGenTitle.Text.Replace("'", "''") + "'"); if (res.Length != 0) { TimeSpan tsNew = TimeSpan.Zero, tsOld = TimeSpan.Zero; if (txtLength.Text != String.Empty) tsNew = TimeSpan.Parse(txtLength.Text); if (!(res[0]["Length"] is DBNull)) { DateTime date = (DateTime)res[0]["Length"]; tsOld = date.TimeOfDay; } if (tsNew != TimeSpan.Zero && (tsOld == TimeSpan.Zero || tsOld.CompareTo(tsNew) < 0)) { if (tsNew != TimeSpan.Zero && (tsOld == TimeSpan.Zero || tsOld.CompareTo(tsNew) < 0)) res[0]["Length"] = txtLength.Text; if (String.Compare((string)res[0]["Title"], txtTitle.Text, true) != 0) { res[0]["Title"] = txtTitle.Text; res[0]["FullTitle"] = txtGenTitle.Text; } try { dbConn.Open(); adTracks.Update(dt); } catch (Exception err) { MessageBox.Show("Error updating track '" + txtGenTitle.Text + "':\n" + err.Message); return; } finally { dbConn.Close(); } } }
adTracks.Update(dt)
并在“并发冲突:UpdateCommand 影响了预期的 1 条记录中的 0 条”这一行得到一个错误。数据库没有更新,DataTable 也没有。
这可能意味着记录 ID 存在一些错误 - 插入后它没有更新为正确的值。但这里的情况并非如此:OnRowUpdated
在第 2 步的处理程序中使用正确的 ID 更新 ID,并将具有此 ID 的记录添加到表 mdb 文件中。在调用前的第 3 步中adTracks.Update
,res[0]
还包含正确的 ID 值和RowState == Modified
。但我仍然得到这个错误。我究竟做错了什么?
按照此处的建议添加dt.AcceptChanges()
之后- 无济于事。adTracks.Update(dt)
更新:
1. krish建议的尝试方法:
try/catch
我在步骤 3 的块之前添加了以下行:
string cmd = "UPDATE TRACKS SET Length = '" + res[0]["Length"] + "' WHERE ID = " + res[0]["ID"];
adTracks.UpdateCommand = new OleDbCommand(cmd, dbConn);
它有效!数据库已更新并相应DataRow
获取RowState == Unchanged
。这是一个很好的解决方法。但我仍然想知道为什么“传统”方法在这里不起作用。当需要更新许多列时,解决方法不是很方便。此外,它似乎只在我更新单行时是可以接受的,并且我需要一次更新多行的能力。
2. hynsey建议的尝试方法:
我用adTracks.Update(dt);
以下代码替换了第 3 步中的行:
using (OleDbDataAdapter da = new OleDbDataAdapter ("Select * from Tracks", dbConn))
{
OleDbCommandBuilder cb = new OleDbCommandBuilder(da);
da.RowUpdated += new OleDbRowUpdatedEventHandler(OnRowUpdated);
da.Update(dt);
}
可悲的是,行为根本没有改变——同样的错误“并发冲突:UpdateCommand 影响了预期的 1 条记录中的 0 条”。
这是我在所有 3 个步骤中使用的代码(与原始代码比较):
1.
public partial class MainForm : Form
{
public OleDbConnection dbConn = new OleDbConnection();
public DataSet dataset = new DataSet();
protected OleDbDataAdapter adTracks = new OleDbDataAdapter();
ArrayList arrArtists = new ArrayList();
public MainForm(string strFileName)
{
InitializeComponent();
}
private void OnLoad(object sender, EventArgs e)
{
dbConn.ConnectionString = Properties.Settings.Default.dbConnectionString;
try
{
dbConn.Open();
adTracks = new OleDbDataAdapter("Select * from Tracks", dbConn));
adTracks.Fill(dataset,"Tracks");
}
catch (Exception err)
{
MessageBox.Show(err.Message);
return;
}
finally
{
dbConn.Close();
}
cboOriginal.DataSource = dataset.Tables["Tracks"];
cboOriginal.DisplayMember = "FullTitle";
cboOriginal.ValueMember = "ID";
cboOriginal.SelectedIndex = -1;
adTracks.RowUpdated += new OleDbRowUpdatedEventHandler(OnRowUpdated);
}
}
2.
DataTable dt;
DataRow dr;
int newID;
dt = dataset.Tables["Tracks"];
dr = dt.NewRow();
dr["Title"] = txtTitle.Text;
dr["FullTitle"] = txtGenTitle.Text;
dt.Rows.Add(dr);
try
{
dbConn.Open();
using (OleDbDataAdapter da = new OleDbDataAdapter ("Select * from Tracks", dbConn))
{
OleDbCommandBuilder cb = new OleDbCommandBuilder(da);
da.RowUpdated += new OleDbRowUpdatedEventHandler(OnRowUpdated);
da.Update(dataset, "Tracks");
}
}
catch (Exception err)
{
MessageBox.Show("Error adding new track '" + txtGenTitle.Text + "':\n" + err.Message);
return;
}
finally
{
dbConn.Close();
}
res = dt.Select("FullTitle = '" + txtGenTitle.Text.Replace("'", "''") + "'");
if (res.Length != 0)
{
newID = (int)res[0]["ID"];
// continue with newID
}
3.
DataTable dt;
DataRow dr;
DataRow[] res;
dt = dataset.Tables["Tracks"];
res = dt.Select("FullTitle = '" + txtGenTitle.Text.Replace("'", "''") + "'");
if (res.Length != 0)
{
TimeSpan tsNew = TimeSpan.Zero, tsOld = TimeSpan.Zero;
if (txtLength.Text != String.Empty) tsNew = TimeSpan.Parse(txtLength.Text);
if (!(res[0]["Length"] is DBNull))
{
DateTime date = (DateTime)res[0]["Length"];
tsOld = date.TimeOfDay;
}
if (tsNew != TimeSpan.Zero && (tsOld == TimeSpan.Zero || tsOld.CompareTo(tsNew) < 0))
{
if (tsNew != TimeSpan.Zero && (tsOld == TimeSpan.Zero || tsOld.CompareTo(tsNew) < 0)) res[0]["Length"] = txtLength.Text;
if (String.Compare((string)res[0]["Title"], txtTitle.Text, true) != 0)
{
res[0]["Title"] = txtTitle.Text;
res[0]["FullTitle"] = txtGenTitle.Text;
}
try
{
dbConn.Open();
using (OleDbDataAdapter da = new OleDbDataAdapter ("Select * from Tracks", dbConn))
{
OleDbCommandBuildercb = new OleDbCommandBuilder(da);
da.Update(dataset , "Tracks");
}
}
catch (Exception err)
{
MessageBox.Show("Error updating track '" + txtGenTitle.Text + "':\n" + err.Message);
return;
}
finally
{
dbConn.Close();
}
}
}
3. 为了调查为什么我的初始代码不起作用,我提供了有关错误的更多详细信息:
OnRowUpdated
尽管出现错误,还是调用了处理程序,并且我能够检查args
传递给处理程序的参数。args.Row
具有RowState == Modified
并且args.Command
具有以下内容CommandText
(为了便于阅读,我添加了换行符):
UPDATE Tracks SET Length = ? WHERE ((ID = ?) AND
((? = 1 AND Title IS NULL) OR (Title = ?)) AND
((? = 1 AND FullTitle IS NULL) OR (FullTitle = ?)) AND
((? = 1 AND GenreID IS NULL) OR (GenreID = ?)) AND
((? = 1 AND StyleID IS NULL) OR (StyleID = ?)) AND
((? = 1 AND SubStyleID IS NULL) OR (SubStyleID = ?)) AND
((? = 1 AND Length IS NULL) OR (Length = ?)) AND
((? = 1 AND UseOriginal IS NULL) OR (UseOriginal = ?))
AND ((? = 1 AND Version IS NULL) OR (Version = ?)) AND
((? = 1 AND TrackID IS NULL) OR (TrackID = ?)) AND
((? = 1 AND SpecPresConjunctor IS NULL) OR (SpecPresConjunctor = ?)) AND
((? = 1 AND SpecFeatConjunctor IS NULL) OR (SpecFeatConjunctor = ?)) AND
((? = 1 AND FreeRecord IS NULL) OR (FreeRecord = ?)))
谁能告诉这个生成的命令有什么问题?“GenreID”、“StyleID”等是“TRACKS”表中的其他列。我不知道所有这些“?” 意思是。
此外,当异常发生时,堆栈上的最新调用如下:
at System.Data.Common.DbDataAdapter.UpdatedRowStatusErrors(RowUpdatedEventArgs rowUpdatedEvent, BatchCommandInfo[] batchCommands, Int32 commandCount)
at System.Data.Common.DbDataAdapter.UpdatedRowStatus(RowUpdatedEventArgs rowUpdatedEvent, BatchCommandInfo[] batchCommands, Int32 commandCount)
at System.Data.Common.DbDataAdapter.Update(DataRow[] dataRows, DataTableMapping tableMapping)
at System.Data.Common.DbDataAdapter.UpdateFromDataTable(DataTable dataTable, DataTableMapping tableMapping)
at System.Data.Common.DbDataAdapter.Update(DataTable dataTable)
at *my code as above*