2009-04-16 7 views
4

Je construis un framework de notification et pour cela je suis en train de sérialiser et de désérialiser une classe de base, à partir de laquelle dériveront toutes les classes que je veux envoyer.Sérialisation en C# avec une classe dérivée

Le problème est que le code compile, mais lorsque je tente en fait de sérialisation cette classe de base je reçois une erreur disant

System.Runtime.Serialization.SerializationException: Type 'Xxx.DataContracts.WQAllocationUpdate' in Assembly 'Xxx.DataContract, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.

Voici le code:

public class WCallUpdate : NotificationData 
{ 
    private string m_from = ""; 
    [DataMember] 
    public string From 
    { 
     get { return m_from; } 
     set { m_from = value; } 
    } 
    private WCall m_wCall = new WCall(); 
    [DataMember] 
    public WCall Call 
    { 
     get { return m_wCall; } 
     set { m_wCall = value; } 
    } 
} 

Le DataContract pour la La notification est:

/// <summary> 
/// Basic class used in the notification service 
/// </summary> 
[DataContract] 
public class NotificationData 
{ 
} 

/// <summary> 
/// Enum containing all the events used in the application 
/// </summary> 
[DataContract] 
public enum NotificationTypeKey 
{ 
    [EnumMember] 
    Default = 0, 
    [EnumMember] 
    IWorkQueueServiceAttributionAddedEvent = 1, 
    [EnumMember] 
    IWorkQueueServiceAttributionUpdatedEvent = 2, 
    [EnumMember] 
    IWorkQueueServiceAttributionRemovedEvent = 3, 
} 

Le code utilisé pour sérialiser les données est:

#region Create Message 
    /// <summary> 
    /// Creates a memoryStream from a notificationData 
    /// note: we insert also the notificationTypeKey at the beginning of the 
    /// stream in order to treat the memoryStream correctly on the client side 
    /// </summary> 
    /// <param name="notificationTypeKey"></param> 
    /// <param name="notificationData"></param> 
    /// <returns></returns> 
    public MemoryStream CreateMessage(NotificationTypeKey notificationTypeKey, NotificationData notificationData) 
    { 
     MemoryStream stream = new MemoryStream(); 
     BinaryFormatter formatter = new BinaryFormatter(); 
     try 
     { 
      formatter.Serialize(stream, notificationTypeKey); 
      formatter.Serialize(stream, notificationData); 
     } 
     catch (Exception ex) 
     { 
      Logger.Exception(ex); 
     } 
     return stream; 
    } 
    #endregion 

Lorsque je tente de créer un message:

WCallUpdate m_wCallUpdate = new WCallUpdate(); 
NotificationTypeKey m_notificationTypeKey = new NotificationTypeKey.Default; 
CreateMessage(notificationTypeKey , wCallUpdate); 

J'obtenu l'erreur suivante:

System.Runtime.Serialization.SerializationException: Type 'Xxx.DataContracts.WCall' in Assembly 'Xxx.DataContract, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable. 
    at System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type) 
    at System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context) 
    at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo() 
    at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter) 
    at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter) 
    at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo) 
    at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck) 
    at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck) 
    at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph) 
    at Xxx.Notification.NotificationMessageFactory.CreateMessage(NotificationTypeKey notificationTypeKey, NotificationData notificationData) in Xxx.Notification\NotificationCenter.cs:line 36 

Si je mets le drapeau Serializable avant la DataContract on ne résout pas le problème .


merci pour la réponse rapide. Désolé que j'ai oublié de mettre le code du NotificationData (modifié dans le poste principal)

J'ai essayé de mettre l'attribut Serializable à la fois classe sans succès :(

#region NotificationData 
/// <summary> 
/// Basic class used in the notification service 
/// </summary> 
[Serializable] 
[DataContract] 
public class NotificationData 
{ 
} 
#endregion 

et

[Serializable] 
public class WCallUpdate : NotificationData 
{ 
    private string m_from = ""; 
    [DataMember] 
    public string From 
    { 
     get { return m_from; } 
     set { m_from = value; } 
    } 
    private WCall m_wCall = new WCall(); 
    [DataMember] 
    public WCall Call 
    { 
     get { return m_wCall; } 
     set { m_wCall = value; } 
    } 
} 

** Edit: ** Mea culpa afterall :) Vous aviez tous les deux raison. J'ai oublié de propager l'attribut [Serializable] à tous les enfants. Après la mise à jour et la compilation, je n'ai plus l'exception. vous remercie tous les deux pour vos réponses :)


@Marc Gravel: En fait, je pensais à ce que vous suggérez, et a créé le DataContractSerializer, mais je ne suis pas sûr suivante cela fonctionnera? Comme mes cours utilisent d'autres cours? le gros problème avec DataContractSerializer est que vous devez spécifier le type de l'objet que vous voulez sérialiser, et que ma classe utilise d'autres classes comme des champs privés, cela pourrait causer un problème non?

#region DataContractSerializer 
     /// <summary> 
     /// Creates a Data Contract Serializer for the provided type. The type must be marked with 
     /// the data contract attribute to be serialized successfully. 
     /// </summary> 
     /// <typeparam name="T">The type to be serialized</typeparam> 
     /// <returns>A data contract serializer</returns> 
     public static DataContractSerializer CreateDataContractSerializer<T>() where T : class 
     { 
      DataContractSerializer serializer = new DataContractSerializer(typeof(T)); 
      return serializer; 
     } 
     #endregion 
+0

Je suis très confus pourquoi vous utilisez BinaryFormatter avec un contrat de données (ce commentaire maintenant développé dans une réponse). –

Répondre

18

Mettez [Serializable] en haut de la classe. Sérialisable n'est pas nécessairement hérité soit AFAIK. Cela signifie que même si la classe de base a [Serializable], vous en avez toujours besoin sur la classe descendante.

2

Pour obtenir une classe pour être marque sérialisable avec l'attribut sérialisable ou dérivé à partir MarshalByRefObject.

Vous dérivez de NotificationData, est-il sérialisable alors?

Vérifiez également ceci: lorsque des classes de données sérialisables sont placées dans un assembly, vérifiez votre référence de projet ou de fichier dans Visual Studio pour vous assurer que vous obtenez la bonne.

Aussi, si vous signez l'ensemble et le mettez dans le GAC, assurez-vous que l'assemblage dans le GAC est le bon! J'ai rencontré beaucoup de déboguages ​​de temps parce que j'ai mis à jour l'assembly de la version 1.0.0.0 vers la version 1.0.0.1 et oublié de remplacer l'ancien dans le GAC. Les assemblages dans le GAC sont chargés avant les assemblées locales, gardez cela à l'esprit. Et ... la mise en forme binaire est très stricte liée aux versions d'assemblage.

6

Je suis très confus pourquoi vous utilisez BinaryFormatter avec un contrat de données. Il serait normal d'utiliser DataContractSerializer ici ... la logique est alors similaire à l'utilisation [Serializable], sauf que vous avez besoin [DataContract], et il sérialise les membres nommés ([DataMember]), plutôt que les champs avec lesquels travaille BinaryFormatter.

En fait, pour de nombreuses raisons (such as brittleness), je suggère de passer à DataContractSerializer, d'autant que cela semble être votre intention. Ou si vous voulez une forme binaire plus compacte, protobuf-net peut être utile (plus est portable entre les plates-formes, aussi). En aparté - vous n'avez pas besoin de [DataContract] sur enum s - ça ne fait pas de mal, mais ça ne fait pas grand chose non plus.

+0

Marc a raison. Utilisez le DataContractSerializer. Cela vous permettra également de sérialiser en binaire. Voici quelques code: en utilisant (var fs = new FileStream (FILE_NAME, FileMode.Create, FileAccess.Write)) { en utilisant (lecteur var = XmlDictionaryWriter.CreateBinaryWriter (fs)) { var = new dcs DataContractSerializer (typeof (NotificationData)); dcs.WriteObject (lecteur, vrai); } } – CodeWarrior

0

J'ai créé un XList de classe pour y arriver:

AA D1=new AA(); //Derived type 
BB D2=new BB(); //Derived type 
CC D3=new CC(); //Derived type 
X D4=new X(); //Base Class 

XList<X> AllData=new XList<X>(); 
AllData.Add(D1); 
AllData.Add(D2); 
AllData.Add(D3); 
AllData.Add(D4); 
// ----------------------------------- 
AllData.Save(@"C:\Temp\Demo.xml"); 
// ----------------------------------- 
// Retrieve data from XML file 
// ----------------------------------- 
XList<X> AllData=new XList<X>(); 
AllData.Open(@"C:\Temp\Demo.xml"); 
// ----------------------------------- 

Vous trouverez plus de détails here.

Questions connexes