Answered by:

Row not found or changed - LinqDataSource

Question

This appears to be a very annoying bug with LinqDataSource in Orcas Beta 2.

Using ASP.NET data controls, some entity classes refuse to be updated.

This isn't a concurrency thing. It's a simple database table in a development environment. There's no timestamp column. Everything's configured correctly. It only happens with one of the tables (the largest) in my app; the rest work fine.

There's no problem updating objects programmatically with LINQ-to-SQL. I think it's a bug in the LinqDataSource.

Answers

This might be caused by concurrency checks on data that is round-tripped to the client in hidden fields and then reconstituted on the server to be used as 'original' values during the update. Some data types may loose precision when converted to text and back. You can avoid this problem by turning on concurrency checks for this field in the mapping (a propety of the field/column in the designer.)

Monday, September 17, 2007 4:47 PM

All replies

This might be caused by concurrency checks on data that is round-tripped to the client in hidden fields and then reconstituted on the server to be used as 'original' values during the update. Some data types may loose precision when converted to text and back. You can avoid this problem by turning on concurrency checks for this field in the mapping (a propety of the field/column in the designer.)

Add an event handler for the Updating event so that you can inspect the data objects before Update to see what columns are missing. Since the data object is reconstructed on the postback, you will need to make sure that the required original values are roundtripped.

Note that the databound control will roundtrip the DataKeyNames columns and any visible columns. By default the LinqDataSource roundtrips any primary keys or columns required for optimistic concurrency, which can be disabled through the StoreOriginalValuesInViewState flag.

I have a feeling it might have something to do with a null<>string.empty conversion within the LinqDataSource control/viewstate. In my case, the original row had a null value in a nullable column. While handling the OnUpdating event to debug, it looked like it may have been stored as an empty string in the original object.

I was able to work around the exception by giving that column a default of string.empty rather than null -- but this is obviously not ideal in production when there are other dependencies on the data.

The databound controls will extract the values for Update, Insert and Delete from its child controls using 2-way databinding. By default an empty textbox or label will be evaluated as an empty string, not null. In order to treat these values as null, you'll need to use the ConvertEmptyStringToNull property on either a datasource parameter or a bound field (if using GridView or DetailsView).

I am using a FormView to edit a row that already exists with a null value in one column. I suspect that when asp.net generates the original object from LINQ (that will later be compared against the new object from the asp.net form), it might be turning SQL's null value into an empty string.

I tried adding an UpdateParameter as you suggested, but it didn't seem to resolve this issue. I suspect the UpdateParameter only applies to the new object created from the asp.net form -- not the original object created from the database perhaps?

The 'row not found or changed' exception happens when either the primary key isn't set, or when the old values for any columns marked for UpdateCheck do not match what is in the database. The best way to track down which column is causing the exception is by inspecting the OriginalObject in the Updating event args.

The Update parameter's ConvertEmptyStringToNull will affect both the old and new values. You should see the difference when inspecting the OriginalObject and NewObject in the Updating event args.

If the column is marked for UpdateCheck and LinqDataSource has StoreOriginalValuesInViewState enabled (the default), then the original value is the value stored by LinqDataSource during the previous Select. Otherwise, the original value will come from the databound control, if available, using 2-way databinding. The values stored by LinqDataSource will likely match the values from the database, whereas those coming from the databound control may have been converted to empty strings.

Compare the OriginalObject values from the Updating event with those in the database to confirm which column is causing the exception. If you need more help, feel free to send me a repro or discuss further over email: chenriks at microsoft dot com

"If the column is marked for UpdateCheck and LinqDataSource has StoreOriginalValuesInViewState enabled (the default), then the original value is the value stored by LinqDataSource during the previous Select. Otherwise, the original value will come from the databound control, if available, using 2-way databinding. The values stored by LinqDataSource will likely match the values from the database, whereas those coming from the databound control may have been converted to empty strings."

Thanks -- this is exactly what I was looking for. It looks like the "original" values are indeed coming from the ItemTemplate/ItemEditTemplate Label/TextBox controls, and were converted after OnSelected from null to empty string. Adding an UpdateParameter with ConvertEmptyStringToNull="True" does now convert them back to nulls before OnUpdating -- not sure why it didn't seem to be working for me before. Thanks for the help & info!

LinqDataSource is merging the values it gets from the databound control with what it has stored in ViewState. In Beta2, the databound control values take precedence, but in the RTM release the values stored by LinqDataSource will take precedence. For now, this means you will need to rely on the parameter's ConvertEmptyStringToNull property to ensure that these empty strings are converted to null values.

Hey Guys. I just found myself with this same exception and after scanning through this thread I notice something in my code - the varchar column in my db was marked nullable, but the field in my LINQ data context was not. After properly marking
the field as nullable to match the db everything works as normal.