Почему WCF сериализует Enum как строку? - PullRequest
5 голосов
/ 07 июля 2010

код на сервере

<DataContract(Namespace:="http://schema.aam.us.com/2010/6", Name:="TradeStatus")>
Public Enum TradeStatus

    NewOrder = 100
    SendToProvider = 101
    ProviderSubmitted = 102
    ProviderAccepted = 103
    ExecutionPending = 104
    Executed = 105
    TicketsCreated = 106 'TERMINAL STATE

End Enum

<DataContract(Namespace:="http://schema.aam.us.com/2010/6", Name:="StatusUpdate")> _
Public Class StatusUpdate

    Public Sub New(ByVal tradeStatus As TradeStatus, ByVal additionalInformation As String)
        Me.TradeStatus = tradeStatus
        Me.AdditionalInforamtion = additionalInformation
    End Sub

    <DataMember(IsRequired:=True)> _
    Public Property AdditionalInforamtion() As String

    <DataMember(IsRequired:=True)> _
    Public Property TradeStatus() As TradeStatus
End Class

Сгенерированный код

<System.Diagnostics.DebuggerStepThroughAttribute(),  _
 System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0"),  _
 System.Runtime.Serialization.DataContractAttribute(Name:="StatusUpdate", [Namespace]:="http://schema.aam.us.com/2010/6"),  _
 System.SerializableAttribute()>  _
Partial Public Class StatusUpdate
    Inherits Object
    Implements System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged

    <System.NonSerializedAttribute()>  _
    Private extensionDataField As System.Runtime.Serialization.ExtensionDataObject

    Private AdditionalInforamtionField As String

    Private TradeStatusField As String

    <Global.System.ComponentModel.BrowsableAttribute(false)>  _
    Public Property ExtensionData() As System.Runtime.Serialization.ExtensionDataObject Implements System.Runtime.Serialization.IExtensibleDataObject.ExtensionData
        Get
            Return Me.extensionDataField
        End Get
        Set
            Me.extensionDataField = value
        End Set
    End Property

    <System.Runtime.Serialization.DataMemberAttribute(IsRequired:=true)>  _
    Public Property AdditionalInforamtion() As String
        Get
            Return Me.AdditionalInforamtionField
        End Get
        Set
            If (Object.ReferenceEquals(Me.AdditionalInforamtionField, value) <> true) Then
                Me.AdditionalInforamtionField = value
                Me.RaisePropertyChanged("AdditionalInforamtion")
            End If
        End Set
    End Property

    <System.Runtime.Serialization.DataMemberAttribute(IsRequired:=true, EmitDefaultValue:=false)>  _
    Public Property TradeStatus() As String
        Get
            Return Me.TradeStatusField
        End Get
        Set
            If (Object.ReferenceEquals(Me.TradeStatusField, value) <> true) Then
                Me.TradeStatusField = value
                Me.RaisePropertyChanged("TradeStatus")
            End If
        End Set
    End Property

    Public Event PropertyChanged As System.ComponentModel.PropertyChangedEventHandler Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

    Protected Sub RaisePropertyChanged(ByVal propertyName As String)
        Dim propertyChanged As System.ComponentModel.PropertyChangedEventHandler = Me.PropertyChangedEvent
        If (Not (propertyChanged) Is Nothing) Then
            propertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(propertyName))
        End If
    End Sub
End Class

Ответы [ 4 ]

18 голосов
/ 07 июля 2010

Перечисления сериализуются по умолчанию.Как и примитивы и классы Collection, вам не нужно отмечать их [DataContract].Однако это не означает, что WCF не позволяет настраивать поведение сериализации, поэтому в духе взаимодействия вы можете изменить способ сериализации перечисления.Как часть этой настраиваемости, если вы помечаете его с помощью DataContract, но не помечаете EnumMembers, вы меняете схему сериализации по умолчанию.Подробнее о сериализации Enum читайте здесь Сериализация Enum

РЕДАКТИРОВАТЬ: Задумался об этом немного больше, и теперь я начал задумываться об основной причине ... оказывается, это вина WSDL.

По умолчанию, если вы не поставите [DataContract], WCF по умолчанию сериализует перечисление, как если бы оно имело атрибуты [DataContract] и [EnumMembers].Поэтому, если вы возьмете следующий пример

[DataContract]
public enum FileType {
    [EnumMember]
    Text,
    [EnumMember]
    Pdf,
    [EnumMember]
    Word
}

, он сгенерирует следующий WSDL

<xs:simpleType name="FileType">
  <xs:restriction base="xs:string">
    <xs:enumeration value="Text" /> 
    <xs:enumeration value="Pdf" /> 
    <xs:enumeration value="Word" /> 
  </xs:restriction>
</xs:simpleType>
<xs:element name="FileType" nillable="true" type="tns:FileType" />

Так что теперь, если вы уберете атрибуты [EnumMember], например,

[DataContract]
public enum FileType {
    Text,
    Pdf,
    Word
}

ваш WSDL будет выглядеть следующим образом:

<xs:simpleType name="FileType">
  <xs:restriction base="xs:string" /> 
</xs:simpleType>
<xs:element name="FileType" nillable="true" type="tns:FileType" />

Таким образом, второй выглядит так же, как и первый, за исключением элементов перечисления.Теперь, в чем разница между вторым и просто WSDL, описывающим простую строку?Никто.Вот почему ген прокси WCF дает вам строку вместо Enum.

2 голосов
/ 07 июля 2010

Простой - так он и должен работать.

XML-схема не имеет понятия перечисления в смысле пары имя / значение. Наиболее близким из них является возможность указать, что конкретный простой тип может иметь одно из нескольких значений - в данном случае строковые значения.

Обратите внимание, что веб-сервисы ASMX и XML Serializer делают одно и то же.


Хорошо, Джонатан и я оба правы и оба неправы.

При сериализации перечисления WCF добавляет специфичную для .NET информацию в схему XML. Это позволяет другой реализации .NET обрабатывать перечисление как перечисление с полным сохранением значений перечисления.

Однако никакая другая платформа не сможет понять эту информацию. В результате перечисление будет просто рассматриваться как строка, которая может принимать только одно из нескольких значений.

1 голос
/ 23 октября 2015

Поскольку существует определенное Имя для этого конкретного значения.

Если для данного значения не определено имя, то сериализатор запишет целое число, а десериализатор прочитает его очень хорошо:

TradeStatus didntMakeCompileCutoffDate = (TradeStatus)dbRecord.TS; // value 999

Было бы неплохо, если бы сериализатор разрешил опцию записи определенного поля перечисления в виде целого числа, поскольку это позволило бы увеличить список допустимых имен перечисления без необходимости перекомпиляции десериализатора кодВ настоящее время десериализатор создает исключение (и отклоняет весь контракт) из-за нераспознанного имени перечисления.

Возможно, можно обойти эту проблему, используя [EnumMember (Value = "123")] иукажите эквивалентные целые числа для каждого из имен перечислений, известных во время компиляции.Из MSDN Типы перечисления в контрактах данных

Второй способ - указать перечисление контракта как свойство, которое использует целочисленное резервное хранилище, и сериализовать резервное хранилище. SO Link

1 голос
/ 07 июля 2010

Если вы включите атрибут DataContract, то вам нужно пометить хотя бы одно значение атрибутом EnumMember.В противном случае он не может видеть ни одно из значений и превращает все поле в строку.

Если вы не включите атрибут DataContract, то вам также не понадобится атрибут EnumMember.

РЕДАКТИРОВАТЬ: Пример правильного кода

<DataContract(Namespace:="http://schema.aam.us.com/2010/6", Name:="TradeStatus")> 
Public Enum TradeStatus 

    <EnumMember> NewOrder = 100 
    <EnumMember> SendToProvider = 101 
    <EnumMember> ProviderSubmitted = 102 
    <EnumMember> ProviderAccepted = 103 
    <EnumMember> ExecutionPending = 104 
    <EnumMember> Executed = 105 
    <EnumMember> TicketsCreated = 106 'TERMINAL STATE 

End Enum 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...