【问题标题】:Casting to an invalid enum value results in unspecified exception on clientside强制转换为无效的枚举值会导致客户端出现未指定的异常
【发布时间】:2016-03-29 12:29:03
【问题描述】:

我知道将整数转换为无效的枚举值不会导致 C# 中由于标志的异常。在这里,我在 DB 表中指定一个表并使用 EntityFramework 获取其值1-3。在我的服务器端,我将值转换为枚举XyzType,范围从0-2,这显然会生成无效值(XyzType)3。现在我把这个值放在XyzDTO 中并将它发送到 WCF 客户端。反序列化失败并带有这个非常模糊的描述:

现在我已经阅读了其他一些讨论反序列化无效 XML 引起的异常的帖子,这一切都说得通。

我的问题是,为什么我没有得到任何有用的信息来说明原因是什么?我花了最后 1.5 个小时处理这个错误,直到我弄清楚枚举和 DB 值之间的差异。未提供有用信息是否被视为 WCF 错误处理中的缺陷?如果是这样,是否应该通知 MS?您有什么好的做法可以避免将来出现这种情况吗?

编辑:请注意我没有 web.config。我的服务器端是 Windows 服务中的 WCF 服务,即。我有 app.config。

EDIT2:在我看来,关于这个问题以及我尝试过的方法存在一些误解。在调试器中检查 CommunicationException 的 InnerExceptions 根本没有提供任何有用的信息。我也已经在服务器端有“includeExceptionDetailsInFaults”= true,但是服务器端没有抛出异常!它是在客户端反序列化期间引起的。

EDIT3:一些答案建议在客户端激活跟踪。我现在已经尝试过了,但显然对于这个问题,它也没有提供任何有用的信息。 Here 是指向输出的链接。

【问题讨论】:

  • 你能另外显示你的XyzType-Enum吗?
  • 我排除了它,因为它只是值 x,y,z (0,1,2)。
  • 我问过,因为如果您将枚举从 x = 1, y = 2, z = 3 更改为 x = 0, y = 1, z = 2 (与您的数据库匹配),这可能是(尚未更新)客户端的原因抛出异常。

标签: c# wcf enums xml-deserialization


【解决方案1】:

在讨论枚举和 WCF 时,请确保通过使用 DataContract 和 EnumMember 属性标记枚举来序列化/反序列化值,如下所示:

[DataContract(Namespace = Namespaces.V1)]
public enum AreaType : int
{
    [EnumMember]
    Unknown = 0,
    [EnumMember]
    Urban = 1,
    [EnumMember]
    Suburban = 2,
    [EnumMember]
    Rural = 3
}

看起来很明显,但也要注意默认值是 0。正如 khlr 提到的,您可以激活跟踪。在开发过程中,您需要在 web.config 中将 customErrors 模式设置为“关闭”并将调试编译为“true”。

<system.web>
    <compilation debug="true" />
    <customErrors mode="Off" />
</system.web>

一旦您停止开发,您需要提供一个自定义错误页面并设置 debug="false"。

<system.web>
    <compilation debug="false" />
    <customErrors mode="RemoteOnly" defaultRedirect="ErrorRedirectPage.html" />
</system.web>

您还需要对异常处理进行一些思考和设计考虑。您需要确保不会丢失信息。如果您要重新抛出异常,则在异常上。我喜欢使用简单的日志记录框架(例如 NLog)来记录异常,然后我会将异常重新抛出到服务中的原始方法,在该方法中我将异常包装在 FaultException 中,其中 T 是我为其创建的类序列化和保存异常信息。例如:

[DataContract(Namespace=Namespaces.V1)]
public class MyFaultException
{
    [DataMember]
    public string Reason { get; set; }

    public MyFaultException()
    {
        this.Reason = "Service encountered an error";
    }

    public MyFaultException(string reason)
    {
        this.Reason = reason;
    }
}

在我的服务方法中:

try
{
    //...
}
catch (Exception ex)
{
        // Asynchronously log the error message in a helper class using NLog
        LogHelper.LogException(ex, "Error calculating blah.", LogLevel.Error);

        throw new FaultException<MyFaultException>(
                new MyFaultException(ex.Message), new FaultReason(ex.Message));
}

您可能希望定义自己的派生自 Exception 的类来抛出您自己的异常。

【讨论】:

  • 嗨,我没有 web.config。我应该更新我的帖子......很高兴你能详细介绍标准实践,但你会发现它不适用,因为无效的枚举强制转换不会引发异常。然而,改变客户端的错误输出可能是解决方案。
  • 没问题。只是提供一些常见做法的例子。我明白 CommunicationException 不会属于我上面提供的异常处理实践。是的,您可以在客户端捕捉到这一点。我通常会在客户端显式捕获 FaultException、TimeoutException 和 CommunicationException。在 web.config 或在 app.config 中,您需要为 serviceDebug includeExceptionDetailInFaults="True" 包含一个服务行为,正如其他人所提到的。 stackoverflow.com/questions/6906953/wcf-communication-exception
  • 其他人是我自己 :) 那部分是否适用于客户端?我在服务器端使用它
  • 大声笑。是的,我看到了。我用完了字符并试图简洁,但最终没有意义。无论如何,includeExceptionDetailsInFaults 将应用于服务器端配置文件。 MSDN 说,“将 IncludeExceptionDetailInFaults 设置为 true 使客户端能够获取有关内部服务方法异常的信息”。在客户端捕获 InnerException 时检查它。您可能仍需要使用跟踪来查看 CommunicationException 的详细信息。
  • 现在我不想把它告诉你,但我也在我的帖子中指定转换为无效的枚举值不会在服务器端引发异常。因此,设置此属性服务器端不会为我提供任何东西,我只是将它用于开发目的。但在这种情况下它是无用的。当客户端反序列化 XML 内容时会发生异常,直到那一刻才出现。我还检查了调试器中的 CommunicationException。许多嵌套的 InnerExceptions 都没有提供任何有用的信息。
【解决方案2】:

activate tracing 获取有关错误的更详细信息几乎总是一个好主意:

<configuration>
   <system.diagnostics>
      <sources>
            <source name="System.ServiceModel" 
                    switchValue="Information, ActivityTracing"
                    propagateActivity="true">
            <listeners>
               <add name="traceListener" 
                   type="System.Diagnostics.XmlWriterTraceListener" 
                   initializeData= "c:\log\Traces.svclog" />
            </listeners>
         </source>
      </sources>
   </system.diagnostics>
</configuration>

您可以在服务器和客户端的 web 或 app.config 文件中配置跟踪,以从双方获取详细信息。

编辑:

&lt;serviceDebug includeExceptionDetailInFaults="true" /&gt; 使您的服务向客户端传输发生的异常(有关详细信息,请参阅here)。如果客户端不处理该故障,您可能不会注意到它。

【讨论】:

  • 嗨,我没有 web.config。我在客户端和服务器端都有一个 app.config。我已经指定在服务器端包含异常详细信息。您的解决方案是否提供更多详细信息?客户端和服务器的语法是否相同? &lt;serviceBehaviors&gt; &lt;behavior name=""&gt; &lt;serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" /&gt; &lt;serviceDebug includeExceptionDetailInFaults="true" /&gt; &lt;/behavior&gt; &lt;/serviceBehaviors&gt;
  • 我不是那个投反对票的人,但你能澄清一下你的答案吗?我已经在服务器端包含异常详细信息设置为 true。但是您提供的代码是否向我提供了来自客户端或服务器端的更多信息?它是否适用于反序列化失败的客户端?
  • 好吧,我不这么认为;)是的,您可以将它应用到服务器到客户端。它可能会提供非常有用的信息,因为它可能会提供一些关于反序列化的见解,这似乎是您的问题点。
  • 您好,很抱歉,但它仍然无法解决问题。我尝试了您的代码,它所做的只是给我一个包含完全相同无用信息的日志。请参阅问题中的 EDIT3。不过,它看起来确实对其他场景很有用。
  • 听到这个消息很难过。您仍然可以尝试在客户端收到消息并开始反序列化之前检查流量以查看消息。试试Fiddler
【解决方案3】:

您在序列化/反序列化方面遇到问题,并且正如其他人已经建议您需要更多日志记录。而且你已经启用了其中的一些,但不是全部,也不是专门针对序列化的。

尝试使用以下配置进行日志记录:

<configuration>
   <system.diagnostics>
      <sources>
            <source name="System.ServiceModel" 
                    switchValue="Information, ActivityTracing"
                    propagateActivity="true">
               <listeners>
                   <add name="traceListener" />
               </listeners>
            </source>
            <source name="System.ServiceModel.MessageLogging">
               <listeners>
                   <add name="traceListener" />
               </listeners>
            </source>
            <source name="System.Runtime.Serialization" 
                    switchValue="Information">
               <listeners>
                   <add name="traceListener" />
               </listeners>
            </source>
      </sources>
      <sharedListeners>
          <add name="traceListener" 
               type="System.Diagnostics.XmlWriterTraceListener" 
               initializeData= "c:\log\Traces.svclog" />
      </sharedListeners>
    </system.diagnostics>
    <system.serviceModel>
        <diagnostics>
            <messageLogging 
                logEntireMessage="true" 
                logMessagesAtServiceLevel="true"
                maxSizeOfMessageToLog="16777216"/>
        </diagnostics>    
    </system.serviceModel>
</configuration>

在这里我添加了两个额外的跟踪源(并启用了消息日志记录):System.Runtime.Serialization 将产生有关序列化/反序列化过程的任何错误(如果有)。消息记录将帮助您了解您是否在客户端收到任何消息。

从您发布的日志中的调用堆栈中,我很清楚服务器端发生了异常。但是消息记录会更好地显示这一点。

【讨论】:

    【解决方案4】:

    如果你想在 WCF 中使用 Enum,你需要为 Enum定义 0 值这很重要。

    public enum MyEnum{
       Default = 0,
       SomeValue1 = 1,
       SomeValue2= 2,
       ....
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多