I have a system that uses WCF Data Services on top of a .dbml (System.Data.Linq.DataContext) data model. The client retrieves data that it needs into a
System.Data.Services.Client.DataServiceContext, makes changes, and saves back via WCF Data Services, which in turn saves to the SQL Server database. The
DataContext class has an
IUpdateable implementation very similar to this example.
I find this slightly weird and it was built a few years back so there's probably much better ways to do it now, but generally it's all been working well. However, recently it was deployed to a new environment and started having problems along the lines of
An exception of type 'System.Data.Services.Client.DataServiceRequestException' occurred and was caught.
Value of member 'BehaviorAsString' of an object of type 'profile' changed.
A member that is computed or generated by the database cannot be changed.
The problematic column
BehaviorAsString is calculated in the view that is the Source for the entity
profile. In the dbml it's correctly set to ReadOnly=True, since it should never be updated. The stack trace shows that the problem is happening on the server side (as I interpret it - see below).
So far so straightforward: the problem appears to be that my UI is for some reason updating this property, then my DataService is rightly complaining that that's not allowed.
However, I don't think that this is the case. That data model field is only used in one place, a .xaml file, where it's bound to a
TextBlock (i.e. a readonly label). My WPF app isn't changing the field values anywhere. So I think the problem is the column change state gets lost when the objects are (de)serialized. The DataService on the server calls the setter on each field, thus all the fields are treated as dirty and would be saved back to the database. Then LinqToSql notices it's trying to save back a ReadOnly field and throws an exception.
This means I need to do something on the server side to make it not see those readonly fields as changed, or otherwise prevent it from trying to save those changes.
As an attempted hack at a solution I simply commented out the contents of the
BehaviorAsString setter, so it would not be marked as changed. This fixed the immediate exception only for the same problem to occur on another ReadOnly calculated field on a different entity. It's also a terrible solution as it involves modifying an autogenerated file.
It's a client environment so I have limited scope to install different versions and try out different things, so I'd like to have some good options before going back to them again.
The really strange thing though is that this is only happening for this environment and I can't replicate it. It's a different build from one deployed at other clients,
but only a couple of small changes that aren't in files remotely related - I've triple checked this (NB: see update 3 below - could be because of cached DataContext). It's possible that there's a different .NET version installed on the server - the Application Pool is running in the .NET v2.0 runtime, with the app built using .NET 3.5 SP1 - or some other dependency that is different. No other referenced dlls are different.
So my question is twofold:
Given how I'm using WCF Data Services, what's the right way to deal with calculated fields and ensure LINQ doesn't try to save them to the database?
Is there anything environmental that could have caused this problem to arise where it didn't before?
And any other suggestions very welcome!
Stack trace of the exception as it was logged on the client side:
Message: An exception of type 'System.Data.Services.Client.DataServiceRequestException' occurred and was caught.
Type: System.Data.Services.Client.DataServiceRequestException, System.Data.Services.Client, Version=184.108.40.206, Culture=neutral, PublicKeyToken=b77a5c561934e089
Message: An error occurred while processing this request.
System.Data.Services.Client.DataServiceClientException: <?xml version="1.0" encoding="utf-8" standalone="yes"?>
<message xml:lang="en-US">An error occurred while processing this request.</message>
<message>Value of member 'BehaviorAsString' of an object of type 'profile' changed.
A member that is computed or generated by the database cannot be changed.</message>
<stacktrace> at System.Data.Linq.ChangeProcessor.CheckForInvalidChanges(TrackedObject tracked)
at System.Data.Linq.ChangeProcessor.SubmitChanges(ConflictMode failureMode)
at System.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode)
at System.Data.Services.DataService`1.BatchDataService.HandleBatchContent(Stream responseStream)</stacktrace>
TargetSite: Void HandleBatchResponse()
StackTrace: at System.Data.Services.Client.DataServiceContext.SaveResult.HandleBatchResponse()
at System.Data.Services.Client.DataServiceContext.SaveChanges(SaveChangesOptions options)
at RPAdminTool.ViewModel.ViewModelBase.<Save>b__2(Object _, DoWorkEventArgs __)
Possibly this is related (?) which is referred to as a bug but with no reference or further explanation.
I've looked into IUpdateable a little ... could I implement
IUpdatable.SaveChanges() to clear changes on all fields marked as
I did find a code change that I missed previously :-/ which meant that the client-side (WPF) code was re-using a static DataServiceChannel object (my RPDataModelDataContext object) instead of getting a separate one. This was causing another oddity I noticed where despite calling
DataServiceQuery.Expand() some recently-created related entities weren't geting loaded. It turns out the re-use of the DataServiceChannel object meant it wasn't hitting the server/database to get the recently-created entities.
I'm thinking this re-used DataServiceChannel object is probably the cause of this whole issue. I'm still not clear why it would have had this effect, but will update this question with the results of my testing. I'd be very interested in anyone able to answer why it would have this effect, as well as a general way to fix it on the server.