1

I have a class which needs to be a Singleton. It must also be able to load and save its field data in an xml file.

The following method will return a new instance, which breaks my Singleton pattern, leaving potential bugs in my code.

public Settings Load()
{
  using (Stream stream = File.OpenRead(FileName))
  {
    XmlSerializer serializer = new XmlSerializer(typeof(Settings));
    return (Settings)serializer.Deserialize(stream);
  }
}

What method can I use in order to update the data in my existing instance, instead of returning a entirely new instance?

I've studied a bit of Linq to Xml, but haven't found any good example of this. Is it necessary for me to keep all my field data in a Dictionary?

Bob Coder
  • 261
  • 2
  • 12
  • DI container lifetime management is usually a better choice than the Singleton pattern. See http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons – TrueWill Jan 27 '12 at 02:25

2 Answers2

1

I used to run into all sorts of bugs making an Xml Singleton class and ended up scrapping it as I had handles all over the place. I replaced it with using two ways. One a read-only version that was for reading data, and a second Using method/statement for writing changes.

This in general is the pattern I use:

public class Settings : IDisposable
{
    string file = "my settings file";
    XElement root;

    private Settings()
    { 
        root = XElement.Load(file);           
    }

    private void Dispose()
    {
        root.Save(file);
    }

    public static Settings Read { get { return new Settings(); } } // return read-only version

    public static void Write(Action<Settings> handler)
    {
        using(Setting settings = new Settings())
            handler(settings);
    }

    // below here is implentation specific

    public XElement Root { get { return root; } }

    public string SettingA 
    { 
        get { return (string)(Root.Attribute("SettingA") ?? (object)string.Empty); }
        set { Set(Root, "SettingsA", value, true); }
    }

    // I wrote this for another StackOverflow thread
    /// <summary>
    /// Set any value via its .ToString() method.
    /// <para>Returns XElement of source or the new XElement if is an ELEMENT</para>
    /// </summary>
    /// <param name="isAttribute">true for ATTRIBUTE or false for ELEMENT</param>
    /// <returns>source or XElement value</returns>
    private XElement Set(XElement source, string name, object value, bool isAttribute)
    {
        string sValue = value.ToString();
        XElement eValue = source.Element(name), result = source;
        XAttribute aValue = source.Attribute(name);
        if (null != eValue)
            eValue.ReplaceWith(result = new XElement(name, sValue));
        else if (null != aValue)
            aValue.ReplaceWith(new XAttribute(name, sValue));
        else if (isAttribute)
            source.Add(new XAttribute(name, sValue));
        else
            source.Add(result = new XElement(name, sValue));
        return result;
    }

    /// <summary>
    /// Replace with for XAttribute
    /// </summary>
    /// <param name="source"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static XAttribute ReplaceWith(this XAttribute source, XAttribute value)
    {
        XElement parent = source.Parent;
        if (null == parent)
            throw new Exception("Source has no parent");
        source.Remove();
        parent.Add(value);
        return value;
    }

}

I've not used the serializer, so don't know if my pattern will fit for you. I prefer XElement.

So to use this you'd probably write a singleton class that makes use of your non-singleton XmlSerialize class. You'd only access it through the singleton.

But this is how I'd end up using it as is:

string settingA = Settings.Read.SettingA;

To save a value it would be:

Settings.Write(s => s.SettingA = "new value");
Chuck Savage
  • 11,274
  • 6
  • 46
  • 65
0

why dont you have something like

public Class TheClassHoldingYourObject
{
    private static XmlSerializer _instance;
    public static Settings Load() 
    { 
        if(_instance != null) return _instance
        using (Stream stream = File.OpenRead(FileName)) 
        { 
              XmlSerializer serializer = new XmlSerializer(typeof(Settings)); 
              return (Settings)serializer.Deserialize(stream); 
        } 
    }
 }

Now you will always get the same instance

Ivan Crojach Karačić
  • 1,873
  • 2
  • 24
  • 43
  • Having a Container class as a Singleton which would keep a private reference to the actual object I'm interested in seems unwieldy and restrictive. What if I want to reload my "Settings"? In that case there could be two instances of "Settings" in my code when I reload from xml. – Bob Coder Jan 17 '12 at 12:00