2
\$\begingroup\$

Azure Service Fabric uses DataContract serialization, which is a total disaster – it violates LSP (wiki) - see all those KnownTypeAttribute things, making framework development basically impossible.

I made the following class be a parent of all ValueObject to convince Service Fabric actually consume Binary serialization when it thinks about Data Contract one:

[DataContract]
[Serializable]
[JsonObject(MemberSerialization.OptOut)]
public abstract class SerializableObject
{
    [DataMember]
    [JsonIgnore]
    byte[] State { get; set; }
    static object Recursive { get; } = new object();

    [OnSerializing()]
    void OnSerializing(StreamingContext context)
    {
        if (context.Context == Recursive)
            return;

        var formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Other, Recursive));
        using (var stream = new MemoryStream())
        {
            formatter.Serialize(stream, this);
            State = stream.ToArray();
        }
    }

    [OnDeserialized()]
    void OnDeserialized(StreamingContext ctxt)
    {
        if (State == null)
            return;

        var formatter = new BinaryFormatter();
        using (var stream = new MemoryStream(State))
            Assign(formatter.Deserialize(stream));
    }

    void Assign(object obj)
    {
        foreach (var property in GetType()
            .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
            .Where(p => p.CanRead && p.CanWrite))
            property.SetValue(this, property.GetValue(obj));
    }
}

So, actual objects could be something like this:

[DataContract]
[Serializable]
public class PacketRecieved : SerializableObject 
{
    public PacketRecieved(DeviceId deviceId, InboundPacket packet)
    {
        DeviceId = deviceId;
        Packet = packet;
    }

    public DeviceId DeviceId { get; private set; }
    public InboundPacket Packet { get; private set; }
}
\$\endgroup\$
3
\$\begingroup\$
  1. You should probably remove empty braces inside attributes. [OnSerializing()] => [OnSerializing].
  2. This loop is very hard to read due to how you use offsets.

    foreach (var property in GetType()
            .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
            .Where(p => p.CanRead && p.CanWrite))
            property.SetValue(this, property.GetValue(obj));
    

    Mainly because it is impossible to tell at first glace where foreach statement ends and loop body begins. Use offsets to separate them:

    foreach (var property in GetType()
                            .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                            .Where(p => p.CanRead && p.CanWrite))
        property.SetValue(this, property.GetValue(obj));
    

    Or just add { } braces (that's what I would do).

  3. State is probably not the best name for a property, its too generic. Maybe SerializedState or BinaryData would do a better job at explaining its purpose.

  4. It would be nice to have some control over which properties are deserialized. You know, similar to how you can use XmlIgnore attribute to skip a property during xml (de-)serialization.

  5. Unless what you do should be completely obvious to anyone working with Azure, you should add some basic documentation. I am not familiar with Azure, and to me it is unclear what problem you are trying to solve, why do you solve it using inheritance as opposed to using separate non-abstract binary serializer or wtf Recursive property is used for. You might want to explain some of those things in your code using xml-documentation.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.