C# 无法序列化枚举时过早失败或明显抛出

C# 无法序列化枚举时过早失败或明显抛出,c#,wcf,.net-4.0,C#,Wcf,.net 4.0,在WCF服务返回具有无效值(枚举类型中不存在int)的枚举成员的DataContract的场景中,客户端引发的异常是基础连接已关闭:连接意外关闭。 奇怪的是,触发此异常是因为DataContractSerializer无法在连接的服务器端序列化 我宁愿在服务器端的运行时更早地抛出一些更有用、更重要的东西,但可能是一个编译器警告 WCF服务合同 [ServiceContract] public interface IDtoService { [Operati

在WCF服务返回具有无效值(枚举类型中不存在int)的枚举成员的DataContract的场景中,客户端引发的异常是
基础连接已关闭:连接意外关闭。

奇怪的是,触发此异常是因为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; 
            } 
        }

    }
var cl = new DtoServiceClient.DtoServiceClient();
var tada = cl.GetData(1);  // connection closed exception
服务实施

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

    [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; 
            } 
        }

    }
var cl = new DtoServiceClient.DtoServiceClient();
var tada = cl.GetData(1);  // connection closed exception
要引发实际的异常,我必须在调试选项VS2010中取消“仅启用我的代码”,并在异常对话框中为“公共语言运行时异常”取消启用引发

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

序列化异常
枚举值“42”对于类型无效 “WcfService1.Rating”,无法序列化。确保 存在必要的枚举值,并用 如果类型具有DataContractAttribute,则为EnumMemberAttribute属性 属性

这样做的代价是调试器会对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));
是否有让编译器为我发出此检查的选项,或者是否有我不知道的替代选项?
(我还尝试在启用了
检查算术上溢/下溢
的情况下编译,但并不期望它成功)

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

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


此行为是在VS2010/.Net 4.0环境中进行的。

您可以做一些事情

在WCF端,使用
IncludeExceptionDetailInFaults
(使用属性或在您的应用程序中)。这将使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 };

在我看来,在不扩展WCF序列化行为的情况下,契约断言是你能做的最好的了(它可能会变得像牦牛一样毛茸茸的)。@SixtoSaez在深入挖掘了一点之后,我觉得你的建议现在是最实际的。我不想养牦牛……我发现收缩变异法是一种类似的解决方法。谢谢你的调查。你应该注意这不是绝对正确的。如果您从其他地方获取枚举,而不是一个代码常量,那么仍然可能有一个无效值(例如,转换一个解析的int)。是的,这是真的,我注意到并意识到了这种行为。谢谢你指出这一点。