Omer van Kloeten's .NET Zen

Programming is life, the rest is mere details

News

Omer van Kloeten's Facebook profile

Omer has been professionally developing applications over the past 8 years, both at the IDF’s IT corps and later at the Sela Technology Center, but has had the programming bug ever since he can remember himself.
As a senior developer at NuConomy, a leading web analytics and advertising startup, he leads a wide range of technologies for its flagship products.

Get Firefox


powered by Dapper 

.NET Resources

Articles :: CodeDom

Articles :: nGineer

Culture

Projects

Linq to SQL: ChangeConflictException With "WHERE 0 = 1"

I just finished debugging a very annoying error, where I kept getting a ChangeConflictException with the message "Row not found or changed" while trying to update my data. I found that there were no Member Conflicts in the exception, which seemed really weird to me.

When I set the Log property on my context, I found that Linq to SQL was using UPDATE statements that looked like this:

UPDATE [dbo].[MyTable]
SET [Col1] = @p0, [Col2] = @p1
WHERE 0 = 1
-- @p0: Input ...
-- @p1: Input ...

This was apparently some optimization made by the Linq to SQL team, but it gave me a clue as to where to look.

Apparently, when you have a Linq to SQL object model that differs by even one property from your database, you're prone to get this. I found out that two of my fields that were in the SET part of the WHERE clause were defined as NOT NULL in my object model, but were marked as nullable in my database model. Quick fix and all works well.

One has to wonder why there is no "Synchronize" button in the Linq to SQL designer.

Also, here's an interesting piece of code I wrote while debugging, which serializes a ChangeConflictException to string:

public static string SerializeForLog(this ChangeConflictException e, DataContext context)
{
    StringBuilder builder = new StringBuilder();

    using (StringWriter sw = new StringWriter(builder))
    {
        sw.WriteLine("Optimistic concurrency error:");
        sw.WriteLine(e.Message);

        foreach (ObjectChangeConflict occ in context.ChangeConflicts)
        {
            Type objType = occ.Object.GetType();
            MetaTable metatable = context.Mapping.GetTable(objType);
            object entityInConflict = occ.Object;

            sw.WriteLine("Table name: {0}", metatable.TableName);

            var noConflicts =
                from property in objType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                where property.CanRead &&
                      property.CanWrite &&
                      property.GetIndexParameters().Length == 0 &&
                      !occ.MemberConflicts.Any(c => c.Member.Name != property.Name)
                orderby property.Name
                select property;

            foreach (var property in noConflicts)
            {
                sw.WriteLine("\tMember: {0}", property.Name);
                sw.WriteLine("\t\tCurrent value: {0}", 
                    property.GetGetMethod().Invoke(occ.Object, new object[0]));
            }

            sw.WriteLine("\t-- Conflicts Start Here --", metatable.TableName);

            foreach (MemberChangeConflict mcc in occ.MemberConflicts)
            {
                sw.WriteLine("\tMember: {0}", mcc.Member.Name);
                sw.WriteLine("\t\tCurrent value: {0}", mcc.CurrentValue);
                sw.WriteLine("\t\tOriginal value: {0}", mcc.OriginalValue);
                sw.WriteLine("\t\tDatabase value: {0}", mcc.DatabaseValue);
            }
        }

        sw.WriteLine();
        sw.WriteLine("Attempted SQL: ");

        TextWriter tw = context.Log;

        try
        {
            context.Log = sw;
            context.SubmitChanges();
        }
        catch (ChangeConflictException)
        {
            // This is what we wanted.
        }
        catch
        {
            sw.WriteLine("Unable to recreate SQL!");
        }
        finally
        {
            context.Log = tw;
        }

        sw.WriteLine();

        sw.WriteLine(e.SerializeForLog());
    }

    return builder.ToString();
}

Comments

Petr said:

Thanks, it really helped.

# July 17, 2008 12:39 PM

Marcel said:

I've spent a day on this issue. Thank you!

# July 21, 2008 12:06 AM

Andreas said:

Thanks, I was getting these off and on and couldn't figure it out. Great work!

# September 17, 2008 9:59 AM

laurence timms said:

Just another word of thanks. Ran into this problem with some properties I'd added to dropthings widgets and your code helped pinpoint where I'd gone wrong. Cheers!

# October 8, 2008 11:22 AM

Matthew Wills said:

I am having issues with:

sw.WriteLine(e.SerializeForLog());

Is this code dependent on another method perhaps?

# November 3, 2008 8:18 PM

Omer van Kloeten said:

It's possible I may have missed that.

It's a method that serializes an exception to a string (simply dumps everything in it into a string). Should be either easy enough to write or you could simply delete the line.

# November 4, 2008 12:19 AM

dave said:

i have seen this post at least 100 times and thought it doesnt help me...

...now i have discovered that one column which doesnt have ANYTHING to do with my update was nullable in the db but not in the dbml - that was the reason!

double or triplecheck your dbml - everything will be fine.

# April 30, 2009 5:20 AM

Andrey Tagaew said:

Thanks Man!!!

I want to add that it works, no matter of whether your real table in db has more columns then in your model.

It fails if some of that columns has invalid null/not null state in comparing with real db

# June 18, 2009 5:51 AM

Borissov said:

Thanks a lot, I spent almost a day seeking for the root cause, and actually it was a NULL/Not NULL difference...

Cheers

# July 3, 2009 7:42 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)