Partager via


Migrer vers protobuf-net (binaire)

La bibliothèque protobuf-net est un sérialiseur basé sur contrat pour .NET qui utilise le format binaire des mémoires tampons de protocoleserialization. L’API suit des modèles .NET classiques, et est globalement comparable à XmlSerializer et DataContractSerializer.

Certains comportements et fonctionnalités de protobuf-net seront notables lors des migrations à partir de BinaryFormatter, et de nombreux scénarios nécessitent l’application d’attributs aux membres.

  • Par défaut, les types publics et non publics sont sérialisables, le sérialiseur attendant un constructeur sans paramètre.
  • protobuf-net nécessite que chaque type sérialisable soit annoté avec un attribut [ProtoContract] ; cet attribut peut éventuellement spécifier la propriété SkipConstructor = true, ce qui supprime la nécessité d’un constructeur particulier.
  • Chaque champ non statique sérialisable et propriété doit être annotée avec un attribut [ProtoMember(int identifier)]. Les noms des membres ne sont pas encodés dans les données. Au lieu de cela, les utilisateurs doivent choisir un entier positif pour identifier chaque membre, qui doit être unique au sein de ce type.
  • L’héritage doit être explicitement déclaré via l’attribut [ProtoInclude(...)] sur chaque type avec des sous-types connus.
  • Les champs en lecture seule sont pris en charge par défaut.
  • Par ailleurs, certains types tuples non attribués sont reconnus par le modèle de constructeur ; un type avec un constructeur qui a des paramètres qui correspondent (par nom) à tous les membres publics déclarés sera interprété comme un tuple, et l’ordre des paramètres sera utilisé pour déduire l’identificateur de ce membre.
  • L’utilisation du package au moment du design protobuf-net.BuildTools est vivement recommandée. Cela offre des avertissements au moment de la compilation concernant les erreurs courantes.

Migration pas à pas

  1. Recherchez toutes les utilisations de BinaryFormatter.
  2. Assurez-vous que les chemins d’accès au code serialization sont couverts par des tests, pour pouvoir vérifier vos modifications et éviter l'introduction de bogues.
  3. Installez le package protobuf-net (et éventuellement protobuf-net.BuildTools).
  4. Recherchez tous les types sérialisés avec BinaryFormatter.
  5. Pour les types que vous pouvez modifier :
    • Annotez avec l’attribut [ProtoContract] tous les types marqués avec [Serializable], ou implémentez l’interface ISerializable. Si ces types ne sont pas exposés à d’autres applications (par exemple, si vous écrivez une bibliothèque) susceptibles d’utiliser différents sérialiseurs comme DataContractSerializer, vous pouvez supprimer les annotations [Serializable] et ISerializable.
    • Pour les types dérivés, appliquez [ProtoInclude(...)] à leurs types de base (voir l’exemple ci-dessous).
    • Pour chaque type qui déclare tout constructeur qui accepte des paramètres, ajoutez un constructeur sans paramètre ou spécifiez SkipConstructor = true sur l’attribut [ProtoContract]. Laissez un commentaire qui explique l’exigence relative à protobuf-net (afin que personne ne le supprime par accident).
    • Marquez tous les membres (champs et propriétés) que vous souhaitez sérialiser avec [ProtoMember(int identifier)]. Tous les identificateurs doivent être uniques au sein d’un type, mais les mêmes nombres peuvent être réutilisés dans les sous-types si l’héritage est activé.
  6. Pour les types que vous ne pouvez pas modifier :
    • Pour les types fournis par .NET lui-même, vous pouvez utiliser l’API ProtoBuf.Meta.RuntimeTypeModel.Default.CanSerialize(Type type) pour vérifier s’ils sont pris en charge en mode natif par protobuf-net.
    • Vous pouvez créer des objets de transfert de données (DTO) dédiés et les mapper en conséquence (vous pouvez utiliser l’opérateur de cast implicite pour cela).
    • Utilisez l’API RuntimeTypeModel pour définir tout ce que les attributs autorisent.
  7. Remplacez l’utilisation de BinaryFormatter par ProtoBuf.Serializer.
-[Serializable]
+[ProtoContract]
+[ProtoInclude(2, typeof(Point2D))]
public class Point1D
{
+   [ProtoMember(1)]
    public int X { get; set; }
}

-[Serializable]
+[ProtoContract]
public class Point2D : Point1D
{
+   [ProtoMember(2)]
    public int Y { get; set; }
}