1

I've problems to serialize and deserialize a singleton via DataContract.

First some facts:

1.) Singleton is "internal"
2.) Singleton contains Dictionaries

My serialization and deserialization works fine, but it isn't the right way for a singleton. If I deserialize the xml, I always generate a new instance of my singleton and overwrite the current reference of the singleton object - but after this it isn't a singleton further.

Has anybody any idea? - Thanks.

Lieven Keersmaekers
  • 57,207
  • 13
  • 112
  • 146
  • It's not really a singleton then is it, if you're overwriting the instance? :) So why bother with the singleton pattern? – Strelok Jul 24 '12 at 14:48
  • Yeah you're right. But I think this is a dirty and not the usual way to make "a singleton a singleton". Or is it? :) – Philipp Matzka Jul 24 '12 at 15:11

5 Answers5

0

Check this link from msdn, there is an example on serializing a singleton. After deserialization you should return the reference and not the object.

  • This is not the right way - I can't use the ISerializer until I'm using Data Contract Attributes. I've get following exception: Type 'ConfigurationData' cannot be ISerializable and have DataContractAttribute attribute. – Philipp Matzka Jul 24 '12 at 15:04
0

Try using NetDataContractSerializer-

The NetDataContractSerializer differs from the DataContractSerializer in one important way: the NetDataContractSerializer includes CLR type information in the serialized XML, whereas the DataContractSerializer does not. Therefore, the NetDataContractSerializer can be used only if both the serializing and deserializing ends share the same CLR types.

The serializer can serialize types to which either the DataContractAttribute or SerializableAttribute attribute has been applied. It also serializes types that implement ISerializable.

Code example:

[DataContract(Name = "Customer", Namespace = "http://www.contoso.com")]
class Person : IExtensibleDataObject
{
    [DataMember()]
    public string FirstName;
    [DataMember]
    public string LastName;
    [DataMember()]
    public int ID;

    public Person(string newfName, string newLName, int newID)
    {
        FirstName = newfName;
        LastName = newLName;
        ID = newID;
    }

    private ExtensionDataObject extensionData_Value;    
    public ExtensionDataObject ExtensionData
    {
        get { return extensionData_Value; }
        set { extensionData_Value = value; }
    }
}

Searialization:

Person p1 = new Person("Zighetti", "Barbara", 101);
FileStream fs = new FileStream(fileName, FileMode.Create);
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(fs);
NetDataContractSerializer ser = new NetDataContractSerializer();
ser.WriteObject(writer, p1);

Msdn Link here

SSK .NET PRO
  • 126
  • 4
0

(code for .net 4.0)

I had the same problem: the de-serialization needs to create a new instance of the singleton class, which it can(!) do because its inside a member function: the constructor is visible to members, but that instance cannot replace the singleton instance visible from the outside (the "this").

So you have to copy the properties from your de-serialized instance into the "this" instance.

Doing the copying by hand gets old fast, so here is my solution using reflection to copy the public writable members that are not marked [xmlignore]:

    public static class SerializationHelpers
    {

      /// <summary>
      /// Copy all public props and fields that are not xmlignore
      /// </summary>
      /// <typeparam name="T"></typeparam>
      /// <param name="target"></param>
      /// <param name="other"></param>
      public static void CopyTypeFields<T>(T target, T other)
      {

        // get all public static properties of MyClass type
        PropertyInfo[] propertyInfos = other.GetType().GetProperties(BindingFlags.Public  

        | BindingFlags.Instance);
        FieldInfo[] fis = other.GetType().GetFields(BindingFlags.Public | 
        BindingFlags.Instance);


        foreach (FieldInfo fi in fis)
        {
          if ((fi.Attributes & FieldAttributes.FieldAccessMask) != 
              FieldAttributes.Literal &&
              (fi.Attributes & FieldAttributes.FieldAccessMask) != 
              FieldAttributes.Static)
          {
            if (IsXmlIgnored(fi)) { continue; }
            var myval = fi.GetValue(other);
            fi.SetValue(target, myval);
          }
        }


        foreach (PropertyInfo pi in propertyInfos)
        {
          if (!pi.CanWrite || !pi.CanRead) { continue; }
          if (IsXmlIgnored(pi)) { continue; }

          var myval = pi.GetValue(other, null);
          pi.SetValue(target, myval, null);
        }
      }

      private static bool IsXmlIgnored(MemberInfo pi)
      {
        object[] fiGetCustomAttributes = pi.GetCustomAttributes(false);
        foreach (object ob in fiGetCustomAttributes)
        {
          if (ob.GetType().
              Equals(typeof(System.Xml.Serialization.XmlIgnoreAttribute)))
          {
            return true;
          }
        }
        return false;
      }

    }

    // to use it ...
    // the deserialization method of the singleton mySingleton



    public bool loadSingleton()
    {
      bool ret= false;

      try
      {
        Type myType = GetType();
        XmlSerializer reader = new XmlSerializer(myType);
        try
        {
          using (StreamReader file = new StreamReader(filename))
          {
            try
            {
              mySingleton t1 = (mySingleton)reader.Deserialize(file);
              CopySerializationFields(t1);
              ret= true;
            }

            catch
            {
              ...
            }
          }
        }
        catch
        {
          ...
        }
      }
      catch (Exception ex)
      {
        ...
      }
      return ret;
    }


    private void CopySerializationFields(ProcessingSettings other)
    {
      SerializationHelpers.CopyTypeFields(this, other);
    }
hans
  • 1,001
  • 1
  • 11
  • 14
0

I just searched quite long for a similar solution for atomic classes and have found an answer to a similar problem by Marc Gravell. This also works for singletons.

In your singleton class you implement the GetRealObject method defined by the interface System.Runtime.Serialization.IObjectReference. There you could also add previously serialized data to the singleton if needed and return the static reference as the reference that is used after deserialization.

Here's my example:

[System.Runtime.Serialization.DataContract]
public class MySingletonClass : System.Runtime.Serialization.IObjectReference
{
    private MySingletonClass()
    {
    }

    private static MySingletonClass _Instance;
    public static MySingletonClass Instance
    {
        get
        {
            if (_Instance == null)
                _Instance = new MySingletonClass();
            return _Instance;
        }
    }

    object System.Runtime.Serialization.IObjectReference.GetRealObject(System.Runtime.Serialization.StreamingContext context)
    {
        MySingletonClass realObject = Instance;
        realObject.Merge(this);
        return realObject;
    }

    private void Merge(MySingletonClass otherInstance)
    {
        // do your merging here
    }
}

You can use this for atomic classes too. You only have to change the Instance property to a GetInstance method and call it with the appropriate property in the GetRealObject method (i.e. an ID).

Community
  • 1
  • 1
Andy B.
  • 135
  • 1
  • 9
0
[DataContract]
public sealed class SerializableSingletonPattern
{
    public static SerializableSingletonPattern Instance { get; private set; } = new SerializableSingletonPattern();
    [DataMember] public bool YourData { get; private set; }

    // explicit static constructor so C# compiler will not mark type as beforefieldinit
    static SerializableSingletonPattern() { }

    SerializableSingletonPattern() // your constructor
    {
    }

    [OnDeserialized]
    void OnDeserialized(StreamingContext context)
    {
        Instance = this;
    }
}