urig
urig

Reputation: 16851

.Net Binary Serialization - How to Limit the Object Graph?

I have an object that I'd like to serialize and distribute between processes as efficiently as possible. The object itself has a reference to another object like so:

public class Foo
{
    // Unique Identifier:
    public int Id;
    public Bar Bar;
}

public class Bar
{
    // Unique Identifier:
    public int Id;
}

The thing is that I only want to serialize Foo and run it over the wire. I'd rather not include Bar in what I send over the wire because it is known on the other side and sending it would waste my "bandwidth".

What I thought of doing is this:

  1. At Serialization time, I'd serialize the reference to Bar (Foo.Bar) as an int containing: Bar.Id (which is a unique identifier for Bar instances)

  2. Only Foo will be sent across the wire (containting the int instead of the Bar property).

  3. At deserialization time I'd get the int, fetch the correct Bar from a repository and put it into the Foo.Bar property.

Is this a valid approach to the task of limiting the object graph being serialized? Is there a better way of doing this?

Upvotes: 1

Views: 732

Answers (3)

leppie
leppie

Reputation: 117330

I'm not sure if I am missing something here.

Just use the NonSerialized attribute on the field you do not want to serialize.

Then look at SerializationSurrogate to fix up the references when deserializing.

I am using a similar approach to binary serialize LINQ2SQL objects.

UPDATE:

Better idea (forgot about this). Use the OnDeserializedAttribute, that's quite easy to use.

Using the Surrogate approach would allow you return another instance of that object, if already loaded in memory. I use this (in IronScheme) for fixing up references to boxed valuetypes that could exist at runtime already.

UPDATE 2:

You can see an example of ISerializationSurrogate in the middle of the following file. (I apologize for the ugliness in advance).

Upvotes: 5

Marc Gravell
Marc Gravell

Reputation: 1064114

What you have described would require ISerializable (custom serialization) with regular BinaryFormatter - however, protobuf-net would allow you to simplify things (so you don't need to handle the details yourself), while usually making the data smaller in the process:

[ProtoContract]
public class Foo : ISerializable
{
    // Unique Identifier:
    [ProtoMember(1)]
    public int Id;
    public Bar Bar;

    [ProtoMember(2)]
    private int? BarProxy {
        get {
           if(Bar == null) return null;
           return Bar.Id;
        }
        set {
           if(value == null) { Bar = null; }
           else { // fetch from repository
             Bar = new Bar(); 
             Bar.Id = value;
           }
        }
    }

    public Foo() { }

    void ISerializable.GetObjectData(SerializationInfo info,
           StreamingContext context) {
        Serializer.Serialize(info, this);
    }
    protected Foo(SerializationInfo info, StreamingContext context) {
        Serializer.Merge(info, this);
    }
}

public class Bar
{
    // Unique Identifier:
    public int Id;
}

For simpler setups:

What serializer are you using?

  • with BinaryFormatter, you mark fields you don't want as [NonSerialized]
  • with XmlSerializer, you mark members you don't want as [XmlIgnore]
  • with DataContractSerializer, you simply don't mark them with [DataContract]

Also - perhaps consider protobuf-net; this has a very efficient binary mechanism; more so than BinaryFormatter

Upvotes: 3

JoshBerke
JoshBerke

Reputation: 67128

That sounds feasible essentially you implement the ISerializable interface on Foo, which will let you control how you serialize Foo.

Then you implement the corresponding constructor and you'll have access to the state you saved and can pull the id out.

Upvotes: 2

Related Questions