当枚举无法序列化时,提前失败或明显抛出

本文关键字:失败 枚举 序列化 | 更新日期: 2023-09-27 18:24:55

在WCF服务返回具有无效值(枚举类型中不存在int)的枚举成员的DataContract的情况下,客户端引发的异常为The underlying connection was closed: The connection was closed unexpectedly.
奇怪的是,触发此异常是因为DataContractSerializer无法在连接的服务器端进行序列化。

我宁愿在服务器端的运行时向我抛出更有用、更重要的东西,但可能是编译器警告。。。

WCF服务合同

    [ServiceContract]
    public interface IDtoService
    {
        [OperationContract]
        MyDto GetData(int value);
    }
    public enum Rating
    {
        None = 0,
        NotSet = 1,
        Somevalue = 34
    }
    [DataContract]
    public class MyDto
    {
        Rating _rate;
        [DataMember]
        public Rating Rating 
        { 
            get { return _rate; } 
            set 
            {
                _rate = value; 
            } 
        }
    }

服务实施

    public class DtoService : IDtoService
    {
        public MyDto GetData(int value)
        {
            var dto = new MyDto {  Rating = (Rating) 42 }; // not in ENUM!
            return dto;
        }
    }

客户端

var cl = new DtoServiceClient.DtoServiceClient();
var tada = cl.GetData(1);  // connection closed exception

为了抛出实际的异常,我不得不取消调试选项VS2010中的"仅启用我的代码",并在"异常"对话框中为"公共语言运行时异常"启用抛出

有了这种设置,有价值的例外是:

序列化异常
枚举值"42"对于类型无效"WcfService1.Rating",并且无法序列化。确保存在必要的枚举值,并用标记EnumMemberAttribute属性(如果类型具有DataContractAttribute)属性

这是以调试器对AFAIK可以忽略的所有其他类型的抛出异常非常嘈杂为代价的。

在没有噪音的情况下,我可以尝试如何抛出此异常

我可以在WCF管道中调整或添加什么吗

一种可能的解决方案是在枚举成员的setter中添加一个额外的Assert:

Debug.Assert(Enum.IsDefined(typeof(Rating), value),"value is not valid for this enum");

我尝试的另一种选择是添加一份合同,希望能发出警告。我找不到比基本上一份Debug.Assert.更好的东西了

System.Diagnostics.Contracts.Contract.Assert(Enum.IsDefined(typeof(Rating), value));

是否有让编译器为我发出此检查的选项,或者是否有我不知道的替代选项
(我也试着在启用check for arithmetic overflow/underflow的情况下编译,但没想到会成功)

CodeContracts确实会发出警告(您必须启用隐式枚举写入义务),尽管这并不能真正帮助

实际值可能不在此枚举值定义的范围内

此行为位于VS2010/.Net 4.0上下文中。

当枚举无法序列化时,提前失败或明显抛出

您可以做一些事情。

在WCF方面,使用IncludeExceptionDetailInFaults(使用ServiceBehavior属性或在app.config中)。这将使WCF向客户端发送详细的异常。请注意,这被认为是一个"不安全"的设置,因为它将服务器堆栈跟踪暴露给客户端,所以您应该只在开发过程中使用它。对于生产,您应该使用WCF错误处理程序来记录所有服务错误(或启用WCF跟踪)。

如果您想在编译时使用代码契约来捕获此错误,您可以使用对象不变量:

public class MyDto
{
    public Rating Rating { get; set; }
    [ContractInvariantMethod]
    void Invariant()
    {
        Contract.Invariant(Enum.IsDefined(typeof(Rating), Rating));
    }
}

如果您启用静态分析,您将收到以下行的警告:

new MyDto {  Rating = (Rating) 42 };