11 Replies - 962 Views - Last Post: 18 March 2016 - 05:41 AM Rate Topic: -----

#1 JacobH   User is offline

  • D.I.C Head
  • member icon

Reputation: 27
  • View blog
  • Posts: 181
  • Joined: 07-September 15

What to do for settings beyond the System.Configuration stuff

Posted 15 March 2016 - 04:31 PM

Hi,

I recently have enjoyed using xml settings files into a UserControl that is added to the configurations drop down(just think combo box). It seems to be getting kind of scattered and I'd like to start being able to have at least a basic system to use for them.

As mentioend above the app configuration file just does not seem right for this and I'd like to make something a little less heavy.

I've clearly not tried this design before and just read up about it - I would just like some input before I start investing time in any one attempt.

    [XmlConfigurationFile("graphics.xml")]
    public class GraphicsConfiguration : XmlConfigurationFile
    {
        // Stuff.
    }

    [XmlConfigurationFile("ignoredpeople.xml")]
    public class IgnoreUsersConfiguration : XmlConfigurationFile
    {
        // Stuff.
    }

    public abstract class XmlConfigurationFile
    {
        public UserControl ConfigurationPanel { get; set; }

        public static T SerializeAndReturn<T>() where T : XmlConfigurationFile
        {
            var fileName = GetFilename<T>();
            return Serializer.ImportFromXmlFile<T>(fileName);
        }

        public static void DeserializeAndSave<T>(T configurationToSave) where T : XmlConfigurationFile
        {
            var fileName = GetFilename<T>();
            Serializer.ExportToXmlFile(configurationToSave, fileName);
        }

        public static string GetFilename<T>() where T : XmlConfigurationFile
        {
            return GetAttribute(typeof (T)).FileName;
        }

        private static XmlConfigurationFileAttribute GetAttribute(Type type)
        {
            return
                (XmlConfigurationFileAttribute)
                    type.GetCustomAttributes(typeof (XmlConfigurationFileAttribute), false).FirstOrDefault();
        }

        [AttributeUsage(AttributeTargets.Class)]
        protected class XmlConfigurationFileAttribute : Attribute
        {
            public string FileName { get; set; }

            public XmlConfigurationFileAttribute(string fileName)
            {
                FileName = fileName;
            }
        }
    }


Is This A Good Question/Topic? 0
  • +

Replies To: What to do for settings beyond the System.Configuration stuff

#2 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 7444
  • View blog
  • Posts: 25,084
  • Joined: 05-May 12

Re: What to do for settings beyond the System.Configuration stuff

Posted 15 March 2016 - 07:11 PM

Can you explain the reason for your design of the SerializeAndReturn() and DederializeAndSave() methods. To me, not only do they not follow the single responsibility principle, but the two responsibilities that they have are opposing concepts.
Was This Post Helpful? 0
  • +
  • -

#3 JacobH   User is offline

  • D.I.C Head
  • member icon

Reputation: 27
  • View blog
  • Posts: 181
  • Joined: 07-September 15

Re: What to do for settings beyond the System.Configuration stuff

Posted 15 March 2016 - 08:27 PM

Can I explain it? Yeah :). Can I justify it? Not really.

The reason it is that way is just because I made a helper class in my common library for these kind of task and got use to a little pattern of how I did things. The helper class is three partial classes all the public static partial class Serialize class. It's split up into Json Xml mostly on local files and then also one for web operations. Here is some snippets from the xml stuff:

Spoiler


So the habit I got in is to do this in a nut shell for each type of major xml object I introduced. Normally it took very little to make them pretty stable, just a bit of sanity checking and verifying input. Some times it took more thought but over-all I have been okay with doing it this way(don't think some one will try and import a xml object from a text file that 100 gigs large? Hell yes they will).

Spoiler


As a result I have likely performed bad habits but they have worked well enough that it has not been at the top of my list to worry about changing. I suck bad enough at enough things already. I'll get there at some point.

This post has been edited by JacobH: 15 March 2016 - 08:37 PM

Was This Post Helpful? 0
  • +
  • -

#4 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 7444
  • View blog
  • Posts: 25,084
  • Joined: 05-May 12

Re: What to do for settings beyond the System.Configuration stuff

Posted 15 March 2016 - 09:39 PM

It looks like you've got the terms serialize and deserialize reversed and that is what is confusing me.

To serialize an object means to take an live in memory storage object and write it out into a durable or transportable format. To deserialize an object means to read the durable or transportable format and to recreate a live in memory object. Or to put it Star Trek terms, when Kirk says "Beam me up, Scotty", the teleporter serializes Kirk's molecules into data that go into the "transfer buffer matrix", and then he get's deserialized out of the buffer and unto the transporter pad.
Was This Post Helpful? 1
  • +
  • -

#5 JacobH   User is offline

  • D.I.C Head
  • member icon

Reputation: 27
  • View blog
  • Posts: 181
  • Joined: 07-September 15

Re: What to do for settings beyond the System.Configuration stuff

Posted 15 March 2016 - 10:37 PM

Quote

It looks like you've got the terms serialize and deserialize reversed

Sorry haha wow.. I really do need to get some good rest tonight I just realized how confusing that post is I made and how exhausted I really am this week. I made it instead an showing actual code snippet due to not having access to my computer currently. Just for future readers sake, I will clarify the general way I do things a little better.

However, if you review my example methods in my "core" helper class only instead, you will see the general idea of how I broke them down better.

The first it to be able to take an object and try's to convert it to a XML/Json string representing the object. Here is the XML one. Perhaps you were expecting "SerializeToXml<T>(T obj)"?.
Spoiler


One use for this is to make it easier to generate configurations. For example, you could a use generic configuration builders and implement a XML version very quickly. A little generic class you might use to implement some of that is below.


Spoiler


Then the other main method for both json and xml is to take a string as the input instead and deserialize it to the object desired instead. This is used mostly to make turning data I collected into the xml/json object a bit easier, rather that is converting an array of bytes to a string and then passing it to to the helper, or downloading a string from a website of a json or xml file and passing that to the helper.

Once again, maybe you were looking for "DeserializeXml";
Spoiler


Then the rest are just a mixture of those two core methods for xml/json such as importing/exporting to files and web pages.

This post has been edited by JacobH: 15 March 2016 - 10:39 PM

Was This Post Helpful? 0
  • +
  • -

#6 JacobH   User is offline

  • D.I.C Head
  • member icon

Reputation: 27
  • View blog
  • Posts: 181
  • Joined: 07-September 15

Re: What to do for settings beyond the System.Configuration stuff

Posted 16 March 2016 - 12:04 PM

I had some decent rest and am pretty happy with my solution today. It is intended to be very lightweight as a lot of tools have very detailed and powerful configration systems, the downside is it's a little too much at times for what you really need.

I hope my code looks much cleaner for people perhaps interested in the same thing who had to look at this mess of a thread ;)/>. Thanks for the help with my confusion SkyDiver.

public interface IConfiguration
    {
        bool IsConfigurationloaded { get; }
        void Save();
        void Load();
        void SaveToFile(string file);
        void LoadFromFile(string file);
    }
	
    public abstract class XmlConfiguration<T> : IConfiguration
    {
        protected XmlConfiguration(tring defaultFilePath)
        {
            DefaultFilePath = defaultFilePath;
        }

        public T CurrentConfiguration { get; set; }

        public string DefaultFilePath { get; }
        public bool IsConfigurationloaded { get; set; }

        public virtual void Save()
        {
           SaveToFile(DefaultFilePath);
        }

        public virtual void Load()
        {
            LoadFromFile(DefaultFilePath);
            IsConfigurationloaded = true;
        }

        public void SaveToFile(string file)
        {
            Serializer.ExportToXmlFile(CurrentConfiguration, file);
        }

        public void LoadFromFile(string file)
        {
            var newSettings = Serializer.ImportFromXmlFile<T>(file);
            CurrentConfiguration = newSettings;
            IsConfigurationloaded = true;
        }
    }

This post has been edited by JacobH: 16 March 2016 - 12:32 PM

Was This Post Helpful? 0
  • +
  • -

#7 Curtis Rutland   User is offline

  • (╯□)╯︵ (~ .o.)~
  • member icon


Reputation: 5106
  • View blog
  • Posts: 9,283
  • Joined: 08-June 10

Re: What to do for settings beyond the System.Configuration stuff

Posted 16 March 2016 - 01:22 PM

I'd just go with overloads for Save and Load. Or really, default parameters. No need to have different named methods when you can just do

protected virtual void Save(string path = null)
{
    if(String.IsNullOrWhiteSpace(path)) path = DefaultFilePath;
    Serializer.ExportToXmlFile(CurrentConfiguration, file);
    IsConfigurationloaded = true;
}


That can be called as Save() or Save(somePathString).
Was This Post Helpful? 1
  • +
  • -

#8 JacobH   User is offline

  • D.I.C Head
  • member icon

Reputation: 27
  • View blog
  • Posts: 181
  • Joined: 07-September 15

Re: What to do for settings beyond the System.Configuration stuff

Posted 16 March 2016 - 06:00 PM

Great point. I do not use overloads enough. I always see the value in it when I see them used, since I can see the wide range of the methods possible uses in one spot with a single name. I always forget to abuse them in meaningful ways though so hopefully this is a reminder.

I've never used null as a default value for the object before in the interface definition before. Would this be along the lines of how to do that/what you were suggesting? A few small changes, null as default param, an event for when the configuration is changed, and also I stripped the interface to its bare minimum which I have always found to be better than the design with MORE hard-defined definitions in it in the long run, despite when it can be attractive or seem to make sense at the time.
    public interface IConfiguration
    {
        void Save(string file = null);
        void Load(string file = null);
    }

    public abstract class XmlConfiguration<T> : IConfiguration
    {
        protected XmlConfiguration(string defaultFilePath)
        {
            DefaultFilePath = defaultFilePath;
        }

        public event EventHandler<T> ConfigurationModified;

        public T CurrentConfiguration { get; set; }

        public bool IsConfigurationloaded { get; set; }

        protected string DefaultFilePath { get; set; }

        public virtual void Save(string filePath = null)
        {
            if (string.IsNullOrWhiteSpace(filePath))
            {
                filePath = DefaultFilePath;
            }

            Serializer.ExportToXmlFile(CurrentConfiguration, filePath);
        }

        public virtual void Load(string filePath = null)
        {
            if (string.IsNullOrWhiteSpace(filePath))
            {
                filePath = DefaultFilePath;
            }

            var newConfiguration = Serializer.ImportFromXmlFile<T>(filePath);
            CurrentConfiguration = newConfiguration;
            OnConfigurationModified(CurrentConfiguration);
        }


        protected virtual void OnConfigurationModified(T e)
        {
            ConfigurationModified?.Invoke(this, e);
        }
    }

Was This Post Helpful? 0
  • +
  • -

#9 Curtis Rutland   User is offline

  • (╯□)╯︵ (~ .o.)~
  • member icon


Reputation: 5106
  • View blog
  • Posts: 9,283
  • Joined: 08-June 10

Re: What to do for settings beyond the System.Configuration stuff

Posted 16 March 2016 - 06:30 PM

Yep. Simple is good, reusing code is good...in the long run, if you follow some simple rules like that, you end up writing more concise and expressive code. Elegant code. Code that is easy to read, refactor, maintain, and debug.

Amateurs see neat language features that allow them to write "short" code and abuse them to make undebuggable garbage.

But with practice, you come up with good code like this. You take what you used to think of as a neat trick and really understand how it was intended to be used, and how it can make your programming life easier.

Ok, I got off on a bit of a positive rant there. Moving on...

Default parameter values are a great trick indeed. In a way, it's like creating every possible permutation of overloads for a particular set of parameters, but with a single implementation.

I do like I using the null as a default pattern. Doesn't work on value types, but with the new language features it's even easier to work with Nullable wrappers.
Was This Post Helpful? 0
  • +
  • -

#10 JacobH   User is offline

  • D.I.C Head
  • member icon

Reputation: 27
  • View blog
  • Posts: 181
  • Joined: 07-September 15

Re: What to do for settings beyond the System.Configuration stuff

Posted 16 March 2016 - 07:33 PM

Thanks for the advice and thanks for the compliment (I think?). I had not used null as a parameter in the interface definition because I had some weird disillusion it would cause all kinds of issues and errors when people used the methods. I was completely oblivious to the fact the classes using the interface will be exposed and be forced to implement it that way and see the null and know what to do in their own case.

In fact I notice it was until very recently it was common for me to be thinking far too much about the implementations of classes even when designed interfaces or abstract classes. I'm not sure why, but I think it was some kind of illusion that by idea by making that class an all in-one and providing a bunch of default behaviors was improving my code re-use and that could not be a bad thing, right? In reality you need to consider that on the other end you make introduce needed code and often even eliminate massive amounts of other-wise reusable code by making it too complex.


Quote

Amateurs see neat language features that allow them to write..

The difference between amateurs and people who are approaching expert levels really starts being defined when you are able to just design your programs with out having to even think about not breaking these rules. You can not even imagine not following those rules. That is a very hard thing to portray to some one who has not put the effort into something that is needed to reach that level.
Was This Post Helpful? 0
  • +
  • -

#11 Curtis Rutland   User is offline

  • (╯□)╯︵ (~ .o.)~
  • member icon


Reputation: 5106
  • View blog
  • Posts: 9,283
  • Joined: 08-June 10

Re: What to do for settings beyond the System.Configuration stuff

Posted 17 March 2016 - 09:13 AM

Yeah, I was actually trying to be nice and give a compliment. That "rant" just sort of came out; it's a parallel to a topic I've been meaning to write about soon.
Was This Post Helpful? 0
  • +
  • -

#12 JacobH   User is offline

  • D.I.C Head
  • member icon

Reputation: 27
  • View blog
  • Posts: 181
  • Joined: 07-September 15

Re: What to do for settings beyond the System.Configuration stuff

Posted 18 March 2016 - 05:41 AM

Some one asked me about the Xml helper so just going to throw all the relevant code I ended up after all the help here, thanks again. Yes I turned to the dark side of brackets which I once hated but it is what it is.

public interface IConfiguration {
        void Save(string file = null);
        void Load(string file = null);
    }

    public abstract class XmlConfiguration<T> : IConfiguration {
        public T CurrentConfiguration { get; set; }
        protected string DefaultFilePath { get; set; }

        public virtual void Save(string filePath = null) {
            if (string.IsNullOrWhiteSpace(filePath)) filePath = DefaultFilePath;
            XmlHelper.ExportToXmlFile(CurrentConfiguration, filePath);
        }

        public virtual void Load(string filePath = null) {
            if (string.IsNullOrWhiteSpace(filePath)) filePath = DefaultFilePath;
            var newConfiguration = XmlHelper.ImportFromXmlFile<T>(filePath);
            CurrentConfiguration = newConfiguration;
            OnConfigurationModified(CurrentConfiguration);
        }

        public event EventHandler<T> ConfigurationModified;

        protected virtual void OnConfigurationModified(T e) {
            ConfigurationModified?.Invoke(this, e);
        }
    }

    public static class XmlUtils {
        public static string ExportToXmlString<T>(T obj) {
            var serializer = new XmlSerializer(typeof (T));
            using (var stringWriter = new StringWriter()) {
                serializer.Serialize(stringWriter, obj);
                return stringWriter.ToString();
            }
        }

        public static T ImportFromXmlString<T>(string serializedObj) {
            var serializer = new XmlSerializer(typeof (T));
            using (var stringWriter = new StringReader(serializedObj)) return (T) serializer.Deserialize(stringWriter);
        }

        public static void ExportToXmlFile<T>(T obj, string path, Encoding encoding) {
            using (var file = new StreamWriter(path, false, encoding)) file.Write(ExportToXmlString(obj));
        }


        public static void ExportToXmlFile<T>(T obj, string path) {
            ExportToXmlFile(obj, path, Encoding.UTF8);
        }

        public static T ImportFromXmlFile<T>(string path, Encoding encoding) {
            using (var file = new StreamReader(path, encoding)) return ImportFromXmlString<T>(file.ReadToEnd());
        }

        public static T ImportFromXmlFile<T>(string path) {
            return ImportFromXmlFile<T>(path, Encoding.UTF8);
        }
    }

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1