我有一个在.dbml(System.Data.Linq.DataContext)数据模型上使用WCF数据服务的系统。客户端将需要的数据检索到System.Data.Services.Client.DataServiceContext中,进行更改,然后通过WCF数据服务保存回去,后者又保存到SQL Server数据库中。 DataContext类具有与this example非常相似的IUpdateable实现。

我觉得这有点奇怪,它是几年前建造的,所以现在可能有更好的方法,但是总体来说一切都很好。但是,最近它已部署到新环境中,并且开始出现以下问题:

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.


有问题的列BehaviorAsString是在作为实体profile的源的视图中计算的。在dbml中,将其正确设置为ReadOnly = True,因为它永远不会被更新。堆栈跟踪显示该问题发生在服务器端(据我解释,请参见下文)。

到目前为止如此简单:问题似乎是我的UI由于某种原因正在更新此属性,然后我的DataService正确地抱怨不允许这样做。

但是,我认为情况并非如此。该数据模型字段仅在一个.xaml文件中使用,该文件绑定到TextBlock(即只读标签)。我的WPF应用程序没有在任何地方更改字段值。所以我认为问题是当对象被(反)序列化时,列更改状态丢失了。服务器上的DataService在每个字段上调用setter,因此所有字段都被视为脏字段,并将被保存回数据库。然后,LinqToSql注意到它正在尝试保存回ReadOnly字段并引发异常。

这意味着我需要在服务器端执行一些操作,以使其看不到已更改的那些只读字段,否则将阻止它尝试保存这些更改。

作为解决方案的尝试,我只是注释了BehaviorAsString设置程序的内容,因此不会将其标记为已更改。这仅解决了立即异常,仅针对同一问题发生在不同实体上的另一个ReadOnly计算字段上的情况。这也是一个糟糕的解决方案,因为它涉及修改自动生成的文件。

这是一个客户端环境,因此我在有限的范围内可以安装不同的版本并尝试不同的方法,因此在重新使用它们之前,我想有一些不错的选择。

不过,真正奇怪的是,这只是在这种环境下发生的,我无法复制它。这是与其他客户端上部署的版本不同的构建,但是只有少量与远程相关的文件中没有的小更改-我对它进行了三重检查(注意:请参阅下面的更新3-可能是由于缓存了DataContext)。服务器上可能安装了其他.NET版本-应用程序池在.NET v2.0运行时中运行,而应用程序使用.NET 3.5 SP1构建-或其他一些不同的依赖关系。没有其他引用的dll是不同的。

所以我的问题是双重的:


考虑到我如何使用WCF数据服务,处理计算字段并确保LINQ不会尝试将它们保存到数据库的正确方法是什么?
是否有任何环境可能导致此问题在以前没有出现过?


和任何其他建议非常欢迎!



在客户端上记录的异常的堆栈跟踪:

Message: An exception of type 'System.Data.Services.Client.DataServiceRequestException' occurred and was caught.
02/13/2014 16:05:20
Type: System.Data.Services.Client.DataServiceRequestException, System.Data.Services.Client, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Message: An error occurred while processing this request.
InnerException(s):
  System.Data.Services.Client.DataServiceClientException: <?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
  <code></code>
  <message xml:lang="en-US">An error occurred while processing this request.</message>
  <innererror>
    <message>Value of member 'BehaviorAsString' of an object of type 'profile' changed.&#xD;
A member that is computed or generated by the database cannot be changed.</message>
    <type>System.InvalidOperationException</type>
    <stacktrace>   at System.Data.Linq.ChangeProcessor.CheckForInvalidChanges(TrackedObject tracked)&#xD;
   at System.Data.Linq.ChangeProcessor.SubmitChanges(ConflictMode failureMode)&#xD;
   at System.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode)&#xD;
   at System.Data.Linq.DataContext.SubmitChanges()&#xD;
   at RPServices.RPDataModel.RPDataModelDataContext.SaveChanges()&#xD;
   at System.Data.Services.DataService`1.BatchDataService.HandleBatchContent(Stream responseStream)</stacktrace>
  </innererror>
</error>

   at System.Data.Services.Client.DataServiceContext.SaveResult.<HandleBatchResponse>d__1e.MoveNext()
Source: System.Data.Services.Client
TargetSite: Void HandleBatchResponse()
StackTrace:    at System.Data.Services.Client.DataServiceContext.SaveResult.HandleBatchResponse()
   at System.Data.Services.Client.DataServiceContext.SaveResult.EndRequest()
   at System.Data.Services.Client.DataServiceContext.SaveChanges(SaveChangesOptions options)
   at RPAdminTool.ViewModel.ViewModelBase.SaveProcess()
   at RPAdminTool.ViewModel.ViewModelBase.<Save>b__2(Object _, DoWorkEventArgs __)


更新1:

this可能与(?)相关,被称为bug,但没有参考或进一步的解释。

更新2:

我已经研究了IUpdateable一点...我可以实现IUpdatable.SaveChanges()来清除所有标记为IsDbGenerated=true的字段上的更改吗?

更新3:

我确实找到了我之前错过的代码更改:-/,这意味着客户端(WPF)代码正在重新使用静态DataServiceChannel对象(我的RPDataModelDataContext对象),而不是获得单独的对象。这引起了另一个奇怪的情况,我注意到尽管调用了DataServiceQuery.Expand(),但一些最近创建的相关实体却没有被加载。事实证明,DataServiceChannel对象的重用意味着它没有访问服务器/数据库来获取最近创建的实体。

我认为这个重复使用的DataServiceChannel对象可能是整个问题的原因。我仍然不清楚为什么会产生这种效果,但是会用我的测试结果来更新这个问题。我对能够回答为什么会产生这种效果以及将其修复在服务器上的一般方法的人非常感兴趣。

最佳答案

您是否通过WCF发送生成的相同实体对象Linq2Sql?

如果要使用非Code First方法修改现有系统,则需要引入数据传输对象(DTO)。这意味着您需要将它们转换为实体对象,并在此确保不会触摸只读字段。

如果使用的是Code First,则可以在模型类的只读字段上设置属性,以告诉序列化程序保持不变。

关于c# - 通过WCF数据服务和LINQ保存数据时,InvalidOperationException“类型为'Y'的对象的成员'X'的值已更改。”,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/22160636/

10-11 10:34
查看更多