2009-09-12 85 views
0

我試圖克服WCF和枚舉的問題,我試圖從服務器傳遞給客戶端(或另一臺服務器),其中包含一個枚舉的對象。有目的地,枚舉從1開始。一切都很好,當枚舉初始化和定義的值,但是當它沒有在枚舉中定義,我得到這個美好的(和非常描述性的(...))錯誤信息:枚舉和WCF - 有意義的錯誤?

「底層連接已關閉:連接意外關閉。「

我試圖實現的是,當我得到這個場景時,無論是從數據庫中的損壞數據(無論如何將其轉換爲enum,這完全相當奇怪),或者當開發人員忘記設置枚舉值時啓動對象,得到一個有意義的消息,如「Enum值無效,鍵入:{0},值:{1}」。

我試着在類的setter和getter中使用「Enum.IsDefined」,並將有意義的異常拋出到客戶端(或其他服務器),但仍然得到「連接關閉「錯誤(當允許調試服務器時,我得到有意義的消息,但僅在服務器端)。

這裏的枚舉二傳手&吸氣的一個片段:

private TestEnum m_TestEnum; 

    [DataMember] 
    public TestEnum TestEnum 
    { 
     get 
     { 
      if (Enum.IsDefined(typeof(TestEnum), m_TestEnum)) 
      { 
       return m_TestEnum; 
      } 
      else 
      { 
       throw new ApplicationException("Enum value is not valid: " + m_TestEnum); 
      } 
     } 
     set 
     { 
      if (Enum.IsDefined(typeof(TestEnum), value)) 
      { 
       m_TestEnum = value; 
      } 
      else 
      { 
       throw new ApplicationException("Enum value is not valid: " + value); 
      } 
     } 
    } 

起價0枚舉(與「未知」值)還不夠好,因爲我仍然可以得到哪些沒有價值存在於枚舉中。我可以結合使用這兩種解決方案,在這裏我檢查「IsDefined」並將枚舉設置爲「未知」值,但仍然 - 這不是理想的解決方案,因爲我們想知道這些情況以便在開發週期中解決它們。

你說什麼? 謝謝, Nir。

+3

只是爲了澄清一些事情:你** **不傳遞對象從客戶端到服務器 - 你傳遞一個**序列化消息**表示它們。客戶端上的所有「對象」都將被序列化,作爲消息發送(基於文本或基於二進制),然後在服務器上再次解除序列化(與迴應的方式相同)。只是要清楚 - 沒有*對象*被傳遞 – 2009-09-12 15:12:26

+0

風格的小點 - 不要在代碼中使用ApplicationException。它不會比拋出'Exception'增加任何價值,因爲從來沒有直接捕獲'ApplicationException'的情況。框架設計師自己建議不要這樣做。這裏有一些信息:http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=56 – 2009-09-12 16:28:23

+0

...相反,你可能會考慮使用'ArgumentOutOfRangeException'作爲setter(無論你是否需要getter或者不取決於默認字段值是否有效 – 2009-09-12 16:29:45

回答

5

通過WCF發送枚舉時,將發送枚舉值的字符串表示形式,而不是數字值。這允許雙方將相同的枚舉標籤映射到不同的數字。

如果收件人無法理解發送的標籤,則該頻道將被關閉。我相信這可以擴展到沒有定義的枚舉的值(它看起來像你正在迎合的)。

所以,僅僅因爲enum標籤在發送方是有效的,並不意味着客戶端一定會理解它。

您是否在發件人和收件人之間共享相同的類定義?

要進一步調試此問題,您應該在客戶端和服務器中啓用服務跟蹤(許多令人沮喪的WCF問題都是如此)。它將幫助您確定問題的根源,否則您無法指示。

添加以下XML到您的App.config(客戶端和服務器上):

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

several options for this configuration

添加此配置後,您將創建一個.svclog文件,可使用Service Trace Viewer Tool查看該文件。此日誌包含有關發生的所有事件的大量信息,包括警告和錯誤。

請注意,生成的日誌文件可能會變得很大,因此一旦完成調試,務必刪除或註釋掉此配置節。


請注意,您可以爲導線上使用的枚舉成員指定不同的字符串值。

例如,下面的兩個枚舉是等價超過WCF:

public enum CarCondition 
{ 
    New, 
    Used, 
    Rental, 
    [NonSerialized] 
    Lost 
}  

[DataContract(Name = "CarCondition")] 
public enum CarConditionWithDifferentNames 
{ 
    [EnumMember(Value = "New")] 
    BrandNew, 
    [EnumMember(Value = "Used")] 
    PreviouslyOwned, 
    [EnumMember] 
    Rental 
} 

您可以閱讀more information about using enums over WCF here

+1

好點 - 因爲枚舉值在消息中傳遞,所以可以設計一個計劃來處理接收端(例如服務器)上的反序列化錯誤,並以某種方式對這些錯誤做出反應。或者可以在服務器上捕獲這些異常,並將它們轉換爲SOAP FaultExceptions,從而避免連接被破壞。 – 2009-09-12 15:14:31

+0

Marc, 請您詳細說明您的想法嗎? - 看起來這是我們的目標。 Drew - 爲了回答你的問題,客戶端和服務器共享相同的枚舉定義,問題是枚舉值集不是它的一部分(或者沒有人設置它,或者我們得到了更改來自某處的數據)。 謝謝, Nir。 – nirpi 2009-09-15 08:10:06

1

嗯,問題是序列化不能序列化一個未在枚舉中定義的值,並且這包括(默認)0值。

例如:

enum E 
{ 
    New = 1, 
    Used 
} 


[OperationContract] 
public E Method() 
{ 
    E e; 
    return e; 
} 

方法A呼叫也將失敗的 「e」 被初始化爲 「E =(E)0」。當序列化試圖完成它的工作時,它在E中找不到匹配0的字段,因此失敗(或生成一個與wsdl不匹配的xml,將值限制爲枚舉成員)。

因此,我們必須確保我們的服務方法總是返回有效值(根據枚舉定義)。

我在MSDN中沒有看到任何關於此的討論,所以這是我的測試結果。

希望它能幫助, 何塞