简体   繁体   English

如何使用自动递增键更新update语句的并发问题?

[英]How do I get rid of the concurrency issue with update statements with an auto-incrementing key?

I have run into the following error message at run-time with my novice's Sql / Visual C# database program: 我在运行时使用我的新手的Sql / Visual C#数据库程序遇到以下错误消息:

Concurrency violation: the UpdateCommand affected 0 of the expected 1 records. 并发冲突:UpdateCommand影响了预期的1条记录中的0条。

I have done some searching around about this issue and have seen several threads about it, but they aren't enough for me to resolve the issue. 我已经做了一些关于这个问题的搜索并且已经看到了几个关于它的线索,但它们还不足以让我解决这个问题。 I noticed people talking about how it can often occur when you're using an auto-incrementing primary key. 我注意到人们在谈论当你使用自动递增主键时经常会发生的事情。 That is the case in this program. 这个程序就是这种情况。 Also there is no multithreading or anything else like that that I am coding into this program. 此外,没有多线程或其他任何类似的东西,我正在编写这个程序。 Therefore I think the auto-increment is possibly the only problem. 因此我认为自动增量可能是唯一的问题。

That being said, how do I get the update command to work, despite the auto-increment? 话虽这么说,尽管自动增加,我如何使更新命令工作? I can hardly stand on two feet when it comes to database programming, so please be detailed, if you don't mind. 在数据库编程方面我几乎站不起来,所以如果你不介意的话请详细说明。 Also the command I'm using is via a SqlCommandBuilder object. 我正在使用的命令是通过SqlCommandBuilder对象。 I have set that object to new SqlCommandBuilder(DataAdapter), and I have not done anything special with it. 我已将该对象设置为新的SqlCommandBuilder(DataAdapter),并且我没有对它做任何特殊操作。

Thanks. 谢谢。


This edit is the second one. 这个编辑是第二个。 The first one is below. 第一个在下面。

Due to my inexperience with database programming, I am unable to say this for sure. 由于我对数据库编程经验不足,我无法肯定地说出来。 However I have good reason to believe that the problem I am experiencing has to do with new rows not getting added to the database completely until the program terminates. 但是我有充分的理由相信我遇到的问题与新的行没有完全添加到数据库,直到程序终止。 I do not understand why they are waiting until program termination to do that, or if they are waiting until then, just what exactly what about the program's termination causes them to suddenly get saved completely. 我不明白他们为什么要等到程序终止才能这样做,或者如果他们等到那时,那么程序的终止到底是什么导致它们突然被完全保存。 However I have forgotten to mention that this error only occurs on rows that I have added during that specific execution of the program. 但是我忘了提到这个错误只发生在我在程序的特定执行过程中添加的行上。 If the row was already added on a previous execution or through pre-existing table data, everything's fine. 如果该行已经在先前的执行中添加或者通过预先存在的表数据添加,那么一切都很好。 I am getting the same error with the delete method, and it also only occurs with new rows. 我使用delete方法得到了同样的错误,它也只出现在新行中。

How do I get these rows to be fully saved to everything so that this doesn't happen? 如何将这些行完全保存到所有内容中,以免发生这种情况? What about the program's termination is causing these rows to get fully saved? 该程序的终止是什么导致这些行被完全保存? Thanks! 谢谢!


Due to a request, I have left here two code snippets. 由于请求,我在这里留下了两个代码片段。 The first one will be the method in which the the problem occurs. 第一个是问题发生的方法。 The next one will include the entire class. 下一个将包括整个班级。 There are only two classes in the entire program, and the other class doesn't seem important to me in this particular issue. 整个程序中只有两个类,而另一个类在这个特定问题中对我来说似乎并不重要。

    private void btnUpdate_Click(object sender, EventArgs e)
    {
        if (recordShown)
        {
            con.Open();

            currentRow[1] = tbFirstName.Text;
            currentRow[2] = tbMiddleName.Text;
            currentRow[3] = tbLastName.Text;
            currentRow[4] = tbSuffix.Text;
            currentRow[5] = tbHomePhone.Text;
            currentRow[6] = tbCellPhone.Text;
            currentRow[7] = tbOtherPhone.Text;
            currentRow[8] = tbStreetAddress.Text;
            currentRow[9] = tbCityAndState.Text;
            currentRow[10] = tbCountry.Text;
            currentRow[11] = tbEmail.Text;

            dAdapter.Update(dataset, "Contacts");

            con.Close();
        }
        else
        {
            MessageBox.Show("Please locate/add a record first.");
        }
    }

Next snippet: 下一个片段:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;

namespace Dakota
{
public partial class Form1 : Form
{
    SqlConnection con;
    DataSet dataset;
    SqlDataAdapter dAdapter;
    DataRow currentRow;
    string primaryKey;
    SqlCommandBuilder cmdBuilder;
    bool recordShown = false;

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        dataset = new DataSet();
        con = new SqlConnection();

        con.ConnectionString = "Data Source=.\\SQLEXPRESS;" +
            "AttachDbFilename=C:\\Users\\Sterling\\Documents\\Contacts.mdf;" +
            "Integrated Security=True;Connect Timeout=30;User Instance=True";

        con.Open();

        string getData = "SELECT * FROM tblContacts";
        dAdapter = new SqlDataAdapter(getData, con);
        dAdapter.Fill(dataset, "Contacts");
        cmdBuilder = new SqlCommandBuilder(dAdapter);
        cmdBuilder.ConflictOption = ConflictOption.OverwriteChanges;

        con.Close();
    }

    private void clearTextBoxes()
    {
        tbFirstName.Clear();
        tbMiddleName.Clear();
        tbLastName.Clear();
        tbSuffix.Clear();
        tbHomePhone.Clear();
        tbCellPhone.Clear();
        tbOtherPhone.Clear();
        tbStreetAddress.Clear();
        tbCityAndState.Clear();
        tbCountry.Clear();
        tbEmail.Clear();
    }

    private void fillTextBoxes(int row)
    {
        DataRow dr = dataset.Tables["Contacts"].Rows[row];
        tbFirstName.Text = dr.ItemArray.GetValue(1).ToString();
        tbMiddleName.Text = dr.ItemArray.GetValue(2).ToString();
        tbLastName.Text = dr.ItemArray.GetValue(3).ToString();
        tbSuffix.Text = dr.ItemArray.GetValue(4).ToString();
        tbHomePhone.Text = dr.ItemArray.GetValue(5).ToString();
        tbCellPhone.Text = dr.ItemArray.GetValue(6).ToString();
        tbOtherPhone.Text = dr.ItemArray.GetValue(7).ToString();
        tbStreetAddress.Text = dr.ItemArray.GetValue(8).ToString();
        tbCityAndState.Text = dr.ItemArray.GetValue(9).ToString();
        tbCountry.Text = dr.ItemArray.GetValue(10).ToString();
        tbEmail.Text = dr.ItemArray.GetValue(11).ToString();
    }

    private void fillTextBoxes(DataRow dr)
    {
        tbFirstName.Text = dr.ItemArray.GetValue(1).ToString();
        tbMiddleName.Text = dr.ItemArray.GetValue(2).ToString();
        tbLastName.Text = dr.ItemArray.GetValue(3).ToString();
        tbSuffix.Text = dr.ItemArray.GetValue(4).ToString();
        tbHomePhone.Text = dr.ItemArray.GetValue(5).ToString();
        tbCellPhone.Text = dr.ItemArray.GetValue(6).ToString();
        tbOtherPhone.Text = dr.ItemArray.GetValue(7).ToString();
        tbStreetAddress.Text = dr.ItemArray.GetValue(8).ToString();
        tbCityAndState.Text = dr.ItemArray.GetValue(9).ToString();
        tbCountry.Text = dr.ItemArray.GetValue(10).ToString();
        tbEmail.Text = dr.ItemArray.GetValue(11).ToString();
    }

    private void btnSearch_Click(object sender, EventArgs e)
    {
        string searchFor = tbSearchFor.Text;
        string column;
        if (rbFirstName.Checked)
        {
            column = "firstName";
        }
        else
        {
            column = "lastName";
        }

        DataRow[] rows = dataset.Tables["Contacts"].Select(column + "='" + searchFor + "'");
        int number = rows.Length;

        if (number == 0)
        {
            MessageBox.Show("No such records were found.");
        }
        else if (number > 1)
        {
            string[] strings = new string[rows.Length];
            for (int i = 0; i < strings.Length; i++)
            {
                bool hasFirst = false;
                bool hasMiddle = false;

                strings[i] = "";
                if (rows[i].ItemArray.GetValue(1).ToString() != "")
                {
                    hasFirst = true;
                    strings[i] += rows[i].ItemArray.GetValue(1).ToString();
                }
                if (rows[i].ItemArray.GetValue(2).ToString() != "")
                {
                    hasMiddle = true;
                    if (hasFirst)
                    {
                        strings[i] += " ";
                    }
                    strings[i] += rows[i].ItemArray.GetValue(2).ToString();
                }
                if (rows[i].ItemArray.GetValue(3).ToString() != "")
                {
                    if ((hasFirst && !hasMiddle) || (hasMiddle))
                    {
                        strings[i] += " ";
                    }
                    strings[i] += rows[i].ItemArray.GetValue(3).ToString();
                }
                if (rows[i].ItemArray.GetValue(4).ToString() != "")
                {
                    strings[i] += " " + rows[i].ItemArray.GetValue(4).ToString();
                }
            }

           // int choice;
            Form2 form2 = new Form2(strings);
            if (form2.ShowDialog(this) == DialogResult.OK)
            {
                primaryKey = rows[form2.choice].ItemArray.GetValue(0).ToString();
               // choice = form2.choice;
                fillTextBoxes(rows[form2.choice]);
                currentRow = rows[form2.choice];
                recordShown = true;
            }
        }            
        else
        {
            primaryKey = rows[0].ItemArray.GetValue(0).ToString();
            currentRow = rows[0];
            fillTextBoxes(rows[0]);
            recordShown = true;
        }
    }

    private void btnAdd_Click(object sender, EventArgs e)
    {
        con.Open();

        DataRow row = dataset.Tables["Contacts"].NewRow();
        row[1] = tbFirstName.Text;
        row[2] = tbMiddleName.Text;
        row[3] = tbLastName.Text;
        row[4] = tbSuffix.Text;
        row[5] = tbHomePhone.Text;
        row[6] = tbCellPhone.Text;
        row[7] = tbOtherPhone.Text;
        row[8] = tbStreetAddress.Text;
        row[9] = tbCityAndState.Text;
        row[10] = tbCountry.Text;
        row[11] = tbEmail.Text;
        currentRow = row;

        dataset.Tables["Contacts"].Rows.Add(row);
        dAdapter.Update(dataset, "Contacts");
        recordShown = true;

        con.Close();
    }

    private void btnUpdate_Click(object sender, EventArgs e)
    {
        if (recordShown)
        {
            con.Open();

            currentRow[1] = tbFirstName.Text;
            currentRow[2] = tbMiddleName.Text;
            currentRow[3] = tbLastName.Text;
            currentRow[4] = tbSuffix.Text;
            currentRow[5] = tbHomePhone.Text;
            currentRow[6] = tbCellPhone.Text;
            currentRow[7] = tbOtherPhone.Text;
            currentRow[8] = tbStreetAddress.Text;
            currentRow[9] = tbCityAndState.Text;
            currentRow[10] = tbCountry.Text;
            currentRow[11] = tbEmail.Text;

            dAdapter.Update(dataset, "Contacts");

            con.Close();
        }
        else
        {
            MessageBox.Show("Please locate/add a record first.");
        }
    }

    private void btnDelete_Click(object sender, EventArgs e)
    {
        con.Open();

        currentRow.Delete();
        dAdapter.Update(dataset, "Contacts");
        clearTextBoxes();
        recordShown = false;

        con.Close();
    }
}
}

Thanks! 谢谢!

Here is one explanation but I am not sure if it is exactly what you are seeing: 这是一个解释,但我不确定它是否正是你所看到的:

http://blogs.msdn.com/b/spike/archive/2010/04/07/concurrency-violation-the-updatecommand-affected-0-of-the-expected-1-records.aspx http://blogs.msdn.com/b/spike/archive/2010/04/07/concurrency-violation-the-updatecommand-affected-0-of-the-expected-1-records.aspx

This might be more on track and if so points to some missing lines that should come after you declare cmdBuilder: 这可能会更加正常,如果是这样,则指出在声明cmdBuilder之后应该出现的一些缺失行:

dAdapter.UpdateCommand = cmdBuilder.GetUpdateCommand();
dAdapter.InsertCommand = cmdBuilder.GetInsertCommand();
dAdapter.DeleteCommand = cmdBuilder.GetDeleteCommand();

http://www.codeguru.com/forum/archive/index.php/t-337168.html http://www.codeguru.com/forum/archive/index.php/t-337168.html

Also, you might need to call: 此外,您可能需要致电:

dAdapter.Fill(dataset, "Contacts");

before the con.Close() for all three operations (Insert, Update, and Delete). 所有三个操作(插入,更新和删除)的con.Close()之前。


On an unrelated note, you could reduce duplicate code by changing the "fillTextBoxes(int row)" method to be just: 在一个不相关的说明中,您可以通过将“fillTextBoxes(int row)”方法更改为以下来减少重复代码:

private void fillTextBoxes(int row)
{
  DataRow dr = dataset.Tables["Contacts"].Rows[row];
  fillTextBoxes(dr);
}

A couple things, it looks like you are not passing it back the ID of your identity column when calling update. 有几件事情,看起来你在调用update时没有将它传回你的标识列的ID。 Wouldn't it need to know the ID when doing an update? 在进行更新时是不是需要知道ID?

In addition to the comment that srutzky made about the redundant code in fillTextBoxes, you might also consider not referencing your columns by ordinal value and instead reference them by their actual column name. 除了srutzky关于fillTextBoxes中冗余代码的注释之外,您还可以考虑不按顺序值引用列,而是通过实际列名引用它们。 If you were to add a column to your DB, it would break all of your code that is doing things like: 如果要向数据库添加一个列,它将破坏所有正在执行以下操作的代码:

tbLastName.Text = dr.ItemArray.GetValue(3).ToString();

Instead, you might do something like: 相反,您可能会执行以下操作:

tbLastName.Text = dr.ItemArray.GetValue("LastName").ToString();

I don't know offhand if GetValue takes the column name as a parameter, but I'm sure it is something like that. 我不知道GetValue是否将列名作为参数,但我确信它是这样的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM