A perfect blend of all things Dot Net
Yesterday I thought I’d learn about the LinqDataSource in ASP.Net 3.5, and got an interesting surprise.
The new LinqDataSource can also be used with a LINQ-to-SQL model to perform updates. You simply add the DataSource to your page, set the table name, and set EnableUpdate to true. Then, using a standard DataControl, you can make updates to your data entities.
The question is, how does this work? It appears to be a bit magical.
In a previous post, I mentioned that LINQ-to-SQL updates can be done in two ways: either you make a call to retrieve a row, then update it, or you provide known values for all fields and try to update using optimistic concurrency.
If the LinqDataSource used the first technique, the performance might be bad. Plus, it would have to only update those fields that had been updated by the DataControl, and ignore the rest. That wouldn’t make sense, so I correctly supposed that was not what was happening.
It makes more sense that the LinqDataSource would use the values input by the user to make the changes. It could use optimistic concurrency to compare all the entered values.
The problem is, I don’t always display all fields from a particular row on the form, so how would the LinqDataSource know what the missing fields were. Remember, all the fields involved in the update are required for the optimistic concurrency to work. That is usually all the fields in a row, and they aren’t usually all displayed.
So, have a guess. Where does the LinqDataSource store it’s values?
Let’s think now. Where would be the easiest place to keep it? To keep it really simple, I’ll let you ignore all other aspects of building an application, like security or performance.
Yep, you guessed it - ViewState.
The ViewState actually contains all values for all the fields in a LINQ-to-SQL entity by default. Those values are sent to the client, even if the user isn’t supposed to be able to see them.
Aaaarrgghhhh!
Now, let’s not get overexcited. The ViewState is encoded, so it’s not easy to just go and change it. But it’s not impossible. In general, I don’t think it’s such a great idea to have all data sent to a client by default. Someone is going to overlook something one day, and there’ll be a costly mistake.
And what if you display a large grid using the LinqDataSource. Well, you aren’t just getting the ViewState from the grid sent back and forth with your page, but the LinqDataSource is going to store all your data retrieved in the ViewState too.
Bummer!
Great control. Large overhead.
The good news is that the ASP.Net team realized that this was a problem and did something about it.
If you look carefully, you will notice that the LinqDataSource has a property StoreOriginalValuesInViewState. By default (sigh) that property is set to true. If you set it to false, the control won’t work any more. Simple as that.
Here’s what the official documentation has to say about that:
If you set the StoreOriginalValuesInViewState property to false, the original values are not persisted in view state for the data-bound control. In that case, LINQ to SQL cannot verify the integrity of the data. LINQ to SQL will throw an exception that indicates a data conflict even if the data in the data source has not actually changed.
That’s in the remarks section of the LinqDataSource.StoreOriginalValuesInViewState property documentation.
Funnily enough, the same section states the problem with stuffing data into the ViewState:
Storing the original values in view state can cause the page size to become unnecessarily large and can expose sensitive data to a malicious user.
Now, I don’t know about you, but that looks like a pretty serious warning to me. Why isn’t it in a more obvious place, like as a comment that pops in up Intellisense whenever you use a LinqDataSource?
So how do you handle it? How can you use the LinqDataSource?
The answer lies in the generation of the LINQ-to-SQL model which you generate. Each entity field has a Column attribute which allows you to specify if it is used for checking during an optimistic concurrency field comparison.
For example, you could create a Model with the following Property:
[Column(Storage=“_AddressID”, UpdateCheck=UpdateCheck.Never, … public int AddressID …
Notice the UpdateCheck.Never value. That means that the field will not be used in the optimistic concurrency check.
The documentation helps a little again:
By default, when update and delete operations have been enabled, the LinqDataSource control stores the original values for all the records in view state. The LinqDataSource control stores values for all primary keys and all properties not marked with UpdateCheck.Never in the Column attribute. You set the UpdateCheck property of the Column attribute in the O/R Designer.
Admittedly, I haven’t yet tried that, so I don’t really know how if you can change that setting for a field without changing generated code, which would be a big no-no. I’ll just have to get back to you on that.
However, there is a shortcut trick.
Basically, if you add a field of type timestamp to your record, only that field will be used for the concurrency check, and only that field’s data will be stored in the ViewState.
If the underlying data source contains a timestamp field that is automatically updated during an update, you can store only that value in view state. In that case, the timestamp property in the entity class is set to IsVersion=true and all the properties are set to UpdateCheck.Never. Because a timestamp field is automatically updated by the database every time that data in that record changes, LINQ to SQL determines from that value if data has changed. This helps reduce the size of view state, and no sensitive data is exposed. LINQ to SQL will check for data consistency by comparing the timestamp value in view state with the timestamp value in the database.
The documentation is great, if you know where to find it.
So, the answer is, if you use the LinqDataSource to throw a quick application together, make sure you use a timestamp field in your table. Call it UpdatedOn, or something. It will make the use of the LinqDataSource a lot easier, if only because you’ll sleep better not worrying about it.
Leave a reply