Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Data Conflict in LINQ

When making changes using SubmitChanges(), LINQ sometimes dies with a ChangeConflictException exception with the error message Row not found or changed, without any indication of either the row that has the conflict or the fields with changes that are in conflict, when another user has changed some data in that row.

Is there any way to determine which row has a conflict and which fields they occur in, and also is there a way of getting LINQ to ignore the issue and simply commit the data regardless?

Additionally, does anybody know whether this exception occurs when any data in the row has changed, or only when data has been changed in a field that LINQ is attempting to alter?

like image 929
ljs Avatar asked Aug 28 '08 15:08

ljs


5 Answers

Here's a way to see where the conflicts are (this is an MSDN example, so you'll need to heavily customize):

try
{
    db.SubmitChanges(ConflictMode.ContinueOnConflict);
}
catch (ChangeConflictException e)
{
    Console.WriteLine("Optimistic concurrency error.");
    Console.WriteLine(e.Message);
    Console.ReadLine();
    foreach (ObjectChangeConflict occ in db.ChangeConflicts)
    {
        MetaTable metatable = db.Mapping.GetTable(occ.Object.GetType());
        Customer entityInConflict = (Customer)occ.Object;
        Console.WriteLine("Table name: {0}", metatable.TableName);
        Console.Write("Customer ID: ");
        Console.WriteLine(entityInConflict.CustomerID);
        foreach (MemberChangeConflict mcc in occ.MemberConflicts)
        {
            object currVal = mcc.CurrentValue;
            object origVal = mcc.OriginalValue;
            object databaseVal = mcc.DatabaseValue;
            MemberInfo mi = mcc.Member;
            Console.WriteLine("Member: {0}", mi.Name);
            Console.WriteLine("current value: {0}", currVal);
            Console.WriteLine("original value: {0}", origVal);
            Console.WriteLine("database value: {0}", databaseVal);
        }
    }
}

To make it ignore the problem and commit anyway:

db.SubmitChanges(ConflictMode.ContinueOnConflict);
like image 116
TheSmurf Avatar answered Nov 02 '22 20:11

TheSmurf


These (which you could add in a partial class to your datacontext might help you understand how this works:

public void SubmitKeepChanges()
{
    try
    {
        this.SubmitChanges(ConflictMode.ContinueOnConflict);
    }
    catch (ChangeConflictException e)
    {
        foreach (ObjectChangeConflict occ in this.ChangeConflicts)
        {
            //Keep current values that have changed, 
//updates other values with database values

            occ.Resolve(RefreshMode.KeepChanges);
        }
    }
}

public void SubmitOverwrite()
{
    try
    {
        this.SubmitChanges(ConflictMode.ContinueOnConflict);
    }
    catch (ChangeConflictException e)
    {
        foreach (ObjectChangeConflict occ in this.ChangeConflicts)
        {
            // All database values overwrite current values with 
//values from database

            occ.Resolve(RefreshMode.OverwriteCurrentValues);
        }
    }
}

public void SubmitKeepCurrent()
{
    try
    {
        this.SubmitChanges(ConflictMode.ContinueOnConflict);
    }
    catch (ChangeConflictException e)
    {
        foreach (ObjectChangeConflict occ in this.ChangeConflicts)
        {
            //Swap the original values with the values retrieved from the database. No current value is modified
            occ.Resolve(RefreshMode.KeepCurrentValues);
        }
    }
}
like image 34
vzczc Avatar answered Nov 02 '22 19:11

vzczc


I've gotten this error in a circumstance completely unrelated to what the error message describes.

What I did was load a LINQ object via one DataContext, and then tried to SubmitChanges() for the object via a different DataContext - gave this exact same error.

What I had to do was call DataContext.Table.Attach(myOldObject), and then call SubmitChanges(), worked like a charm.

Worth a look, especially if you're of the opinion that there really shouldn't be any conflicts at all.

like image 31
Greg Hurlman Avatar answered Nov 02 '22 18:11

Greg Hurlman


The error "Row not found or changed" also will appear sometimes when the columns or types in the O/R-Designer do not match the columns in the SQL database, especially if one column is NULLable in SQL but not nullable in the O/R-Designer.

So check if your table mapping in the O/R-Designer matches your SQL database!

like image 24
Sam Avatar answered Nov 02 '22 18:11

Sam


Thanks to @vzczc. I found the example you gave very helpful but that I needed to call SubmitChanges again after resolving. Here are my modified methods - hope it helps someone.

    /// <summary>
    /// Submits changes and, if there are any conflicts, the database changes are auto-merged for 
    /// members that client has not modified (client wins, but database changes are preserved if possible)
    /// </summary>
    public void SubmitKeepChanges()
    {
        this.Submit(RefreshMode.KeepChanges);
    }

    /// <summary>
    /// Submits changes and, if there are any conflicts, simply overwrites what is in the database (client wins).
    /// </summary>
    public void SubmitOverwriteDatabase()
    {
        this.Submit(RefreshMode.KeepCurrentValues);
    }

    /// <summary>
    /// Submits changes and, if there are any conflicts, all database values overwrite
    /// current values (client loses).
    /// </summary>
    public void SubmitUseDatabase()
    {
        this.Submit(RefreshMode.OverwriteCurrentValues);
    }

    /// <summary>
    /// Submits the changes using the specified refresh mode.
    /// </summary>
    /// <param name="refreshMode">The refresh mode.</param>
    private void Submit(RefreshMode refreshMode)
    {
        bool moreToSubmit = true;
        do
        {
            try
            {
                this.SubmitChanges(ConflictMode.ContinueOnConflict);
                moreToSubmit = false;
            }
            catch (ChangeConflictException)
            {
                foreach (ObjectChangeConflict occ in this.ChangeConflicts)
                {
                    occ.Resolve(refreshMode);
                }
            }
        }
        while (moreToSubmit);

    }
like image 2
Mark Avatar answered Nov 02 '22 19:11

Mark