迁移至SOSS为DTO添加[Serializable()]后反序列化异常求助
项目从inproc会话迁移至SOSS(State Server Service),给多数模型/DTO添加[Serializable]特性后序列化正常,但其中一个YourDTO出现反序列化错误:
System.Exception: An service exception has occured the exception was System.ServiceModel.Dispatcher.NetDispatcherFaultException: The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:GetAllAttachmentsResult. The InnerException message was 'Error in line 1 position 686. 'EndElement' 'YourDTO' from namespace 'http://schemas.datacontract.org/2004/07/Business.Objects.Message' is not expected. Expecting element '_x003C_AttachmentDate_x003E_k__BackingField'.'. Please see InnerException for more details. ---> System.Runtime.Serialization.SerializationException: Error in line 1 position 686. 'EndElement' 'YourDTO' from namespace 'http://schemas.datacontract.org/2004/07/Business.Objects.Message' is not expected. Expecting element '_x003C_AttachmentDate_x003E_k__BackingField'.
对应的DTO代码(通过NuGet包引入):
namespace Business.Objects.Message { [Serializable()] public class YourDTO { public long Id { get; set; } public FileType Type{ get; set; } public string Name { get; set; } public DateTime AttachmentDate { get; set; } public string UserName { get; set; } public YourDTO() { Id = 0; Name = string.Empty; Type = FileType.Unknown; AttachmentDate = DateTime.MinValue; UserName = string.Empty; } } }
已知信息:
- 已尝试给DTO添加
[DataContract]特性但无效 - 仅修改了web.config中的SessionState为SOSS,并给DTO添加
[Serializable]特性后出现问题
1. 统一序列化器,解决BinaryFormatter与DataContractSerializer冲突
错误里的_x003C_AttachmentDate_x003E_k__BackingField是BinaryFormatter序列化自动属性时生成的编译器字段名,而WCF默认用DataContractSerializer,两者格式不匹配导致反序列化失败。
方案:修改WCF绑定为Binary编码
在web.config的WCF绑定配置中添加binaryEncoding,强制使用BinaryFormatter:
<bindings> <basicHttpBinding> <binding name="YourServiceBinding"> <security mode="None"/> <readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="2147483647"/> <binaryEncoding/> </binding> </basicHttpBinding> </bindings>
确保服务端点使用这个绑定配置。
2. 替换自动属性为显式字段,消除编译器生成的backing field问题
自动属性的backing field是编译器自动生成的,跨程序集(比如NuGet包)时可能出现序列化行为不一致。修改DTO为显式字段:
namespace Business.Objects.Message { [Serializable()] public class YourDTO { private long _id; private FileType _type; private string _name; private DateTime _attachmentDate; private string _userName; public long Id { get { return _id; } set { _id = value; } } public FileType Type{ get { return _type; } set { _type = value; } } public string Name { get { return _name; } set { _name = value; } } public DateTime AttachmentDate { get { return _attachmentDate; } set { _attachmentDate = value; } } public string UserName { get { return _userName; } set { _userName = value; } } public YourDTO() { _id = 0; _name = string.Empty; _type = FileType.Unknown; _attachmentDate = DateTime.MinValue; _userName = string.Empty; } } }
注意:需要更新NuGet包的代码后重新发布,再重新引用。
3. 检查SOSS的Session序列化配置
确认SOSS使用的序列化方式和WCF一致,在web.config的SessionState节点添加serialization mode="Binary":
<sessionState mode="Custom" customProvider="SOSSProvider"> <providers> <add name="SOSSProvider" type="SOSS.Web.SessionState.SossSessionStateProvider, SOSS.Web"/> </providers> <serialization mode="Binary"/> </sessionState>
4. 清理NuGet缓存,确保[Serializable]特性生效
旧的NuGet包缓存可能保留了未添加[Serializable]的DTO版本,导致运行时不生效:
- 执行命令清理缓存:
.NET Framework用nuget locals all -clear,.NET Core/.NET 5+用dotnet nuget locals all --clear - 删除解决方案中的NuGet引用,重新安装最新版本
5. 显式标记[DataContract]和[DataMember],对齐序列化字段名
如果必须用DataContractSerializer,给每个属性指定和backing field一致的名称:
namespace Business.Objects.Message { [Serializable] [DataContract(Name = "YourDTO", Namespace = "http://schemas.datacontract.org/2004/07/Business.Objects.Message")] public class YourDTO { [DataMember(Name = "_x003C_Id_x003E_k__BackingField")] public long Id { get; set; } [DataMember(Name = "_x003C_Type_x003E_k__BackingField")] public FileType Type{ get; set; } [DataMember(Name = "_x003C_Name_x003E_k__BackingField")] public string Name { get; set; } [DataMember(Name = "_x003C_AttachmentDate_x003E_k__BackingField")] public DateTime AttachmentDate { get; set; } [DataMember(Name = "_x003C_UserName_x003E_k__BackingField")] public string UserName { get; set; } public YourDTO() { Id = 0; Name = string.Empty; Type = FileType.Unknown; AttachmentDate = DateTime.MinValue; UserName = string.Empty; } } }
内容的提问来源于stack exchange,提问作者EveryDayDev

