System.Data.Linq.ChangeConflictException: Row not found or changed

Viewed 60077

I am trying to delete a selected gridview row using LINQ (No LINQDataSource).

When the selection is changed, the detailsview binding is changed also. I can add a new entry to the database, but when I added this code to a delete button inside the updatePanel, I got an exception:

try
{           
    var query = from i in db.QuestionModules 
                where i.QuestionModuleID == QuestionModuleID 
                select i;

    QuestionModule o = query.First();
    db.QuestionModules.DeleteOnSubmit(o);
    db.SubmitChanges();
}

This is the exception I get:

System.Data.Linq.ChangeConflictException: Row not found or changed. at
System.Data.Linq.ChangeProcessor.SubmitChanges(ConflictMode
failureMode) at
System.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode)
at System.Data.Linq.DataContext.SubmitChanges() 

I've had this problem for about a week, and no matter what I do, it is still there, and the record doesn't get deleted.

Any ideas on what to do?

19 Answers

OK - it looks as though (in my case at least) the answer was to set all non primary-key column's UpdateCheck property to Never in the DBML file. Doing this immediately cured the problem of "Row not found or changed".

Given the rumour that Microsoft are moth-balling Linq-To-Sql in favor of Entity Framework, one wonders whether these sorts of bugs will be fixed?

I'm having the same problem, and came across this blog, which basically states that Linq-To-Sql has got a problem in it's optimistic concurrency where:

  1. High precision datetime fields are used. The solution is to set UpdateCheck to never for that column your DBML file
  2. GridView columns which are set to invisible are accessing a property on the data object (this second reason makes no sense, but it seems to be all the rage on that blog).

I haven't tried these solutions yet, but will post back here once I have.

Ensure that no columns contain null values in the related table (i.e. the table to be updated).

I was able to resolve this issue by executing databind() on the gridview and datasource during updatepanel postback.

    protected void UpdatePanel1_Load(object sender, EventArgs e)
    {
        GridView1.DataBind();
        LinqDataSource1.DataBind();
    }

I refresh updatepanel each time my selection index changes and it was able to resolve the conflicts.

Hope this helps.

I just wanted to add my scenario for anybody who may have this issue.

We use a custom T4 against our Linq to SQL dbml. We basically just modified the original get/set of string properties to automatically trim and set null.

        get { return _OfficiantNameMiddle.GetValueOrNull(); }
        set 
        {
            value = value.GetValueOrNull();
            if (_OfficiantNameMiddle != value) 
            {
                _IsDirty = true;
                OnOfficiantNameMiddleChanging(value);
                SendPropertyChanging("OfficiantNameMiddle");
                _OfficiantNameMiddle = value;
                SendPropertyChanged("OfficiantNameMiddle");
                OnOfficiantNameMiddleChanged();
            }
        }

Legacy data in our database had some leading/trailing spaces so any concurrency check on these columns failed to result in a match (it was comparing the trimmed value against the non-trimmed database value). It was really easy to profile SQL, grab the SQL and start commenting out the items in the WHERE clause until it started returning a row during the concurrency check.

Luckily, we have a LastUpdatedOn field in our tables that is automatically set via OnValidate(System.Data.Linq.ChangeAction).

    partial void OnValidate(System.Data.Linq.ChangeAction action)
    {
        if (action == System.Data.Linq.ChangeAction.Insert)
        {
            CreatedBy = CurrentUserID;
            CreatedOn = DateTime.Now;
            LastUpdatedBy = CreatedBy;
            LastUpdatedOn = CreatedOn;
        }
        else if (action == System.Data.Linq.ChangeAction.Update)
        {
            LastUpdatedBy = CurrentUserID;
            LastUpdatedOn = DateTime.Now;
        }
    }

In order to bypass the problem, we just set the concurrency check to Never on all columns except the Primary Key columns and LastUpdatedOn column. This worked for us.

For us the problem started when we switched to DateTime2 on the SQL Server side. Just marking fields with Column(DbType = "DateTime2") did not help. So, what happened was that initially on the database side we declared our columns as DateTime2(3) to be "backward compatible" with the old DateTime type and everything seemed to work fine until we noticed that when we use SQL-2-Linq we get the "Row not found or changed" exception in the updates of those date fields. To make the quite-long story short the solution was to do 2 things:

  1. Mark the columns with [Column(DbType = "DateTime2(3)", CanBeNull = false)] to match the database declaration. AND
  2. Trim the extra precision digits from the properties in setters like so:

[Column(DbType = "DateTime2(3)", CanBeNull = false)] public DateTime ModifiedAt { get => _modifiedAt; set => _modifiedAt = value.AddTicks(-(value.Ticks % TimeSpan.TicksPerMillisecond)); }

Related