Create an Xml file from an object - c#

I work as a web developer with a web designer and we usually do like this :
- I create the system , I generate some Xml files
- the designer display the xml files with xslt
Nothing new.
My problem is that I use Xml Serialization to create my xml files, but I never use Deserialization. So I'd like to know if there is a way to avoid fix like these :
empty setter for my property
empty parameter-less constructor
implement IXmlSerializable and throw "notimplementedexception" on deserialization
do a copy of the class with public fields

Ok mis-read your question first time around! Pretty sure there is no way to avoid this. There has to be a parameterless constructor and you can't serialize readonly properties. I think your only other option is DataContractSerializer.

http://blogs.mastronardi.be/Sandro/2007/08/22/CustomXMLSerializerBasedOnReflectionForSerializingPrivateVariables.aspx
This article describes creating a custom XML serialiser so you can serialise private fields - it may take a little bit of moulding to the form that you want, but it's easier than it looks (honest!) and it's a good start to writing your own serialiser / deserialiser that will serialise exactly what you want - and doesn't care about parameterless constructors or writeable properties.
The only other solution I can think of is to make a wrapper class for every serialisable class - but I don't know how good that would be in the long run. I just get the impression it's not good.

I know you don't want to add a parameterless constructor nor setters, but that's the only way to go with using the XmlSerializer. The good news is the parameterless constructor can be private and the setters can be empty and serialization will work. See thus:
namespace Aesop.Dto
{
using System;
using System.Xml.Serialization;
/// <summary>
/// Represents an Organization ID/Name combination.
/// </summary>
[Serializable]
public sealed class Org
{
/// <summary>
/// The organization's name.
/// </summary>
private readonly string name;
/// <summary>
/// The organization's ID.
/// </summary>
private readonly int id;
/// <summary>
/// Initializes a new instance of the <see cref="Org"/> class.
/// </summary>
/// <param name="name">The organization's name.</param>
/// <param name="id">The organization's ID.</param>
public Org(string name, int id)
{
this.name = name;
this.id = id;
}
/// <summary>
/// Prevents a default instance of the <see cref="Org"/> class from
/// being created.
/// </summary>
private Org()
{
}
/// <summary>
/// Gets or sets the organization's name.
/// </summary>
/// <value>The organization's name.</value>
[XmlAttribute]
public string Name
{
get
{
return this.name;
}
set
{
}
}
/// <summary>
/// Gets or sets the organization's ID.
/// </summary>
/// <value>The organization's ID.</value>
[XmlAttribute]
public int ID
{
get
{
return this.id;
}
set
{
}
}
}
}

Ok now i understand it. I don't think there can be any way to do it with XMLSerialization.
XMLSerialization need these information to re-populate the object. It does not know that some user never deserialize data. You might have to write some code to generate XML for your objects.

class Preferences
{
private const string filePreferences = "preferences.xml";
public Preferences() { }
public static Preferences Load()
{
Preferences pref = null;
if (File.Exists(Preferences.FileFullPath))
{
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(Preferences));
using (var xmlReader = new System.Xml.XmlTextReader(Preferences.FileFullPath))
{
if (serializer.CanDeserialize(xmlReader))
{
pref = serializer.Deserialize(xmlReader) as Preferences;
}
}
}
return ((pref == null) ? new Preferences() : pref);
}
public void Save()
{
var preferencesFile = FileFullPath;
var preferencesFolder = Directory.GetParent(preferencesFile).FullName;
using (var fileStream = new FileStream(preferencesFile, FileMode.Create))
{
new System.Xml.Serialization.XmlSerializer(typeof(Preferences)).Serialize(fileStream, this);
}
}
}

Related

Expose a Collection But Exclude Parts

In a parent class, I have a collection. In a child class, I want to expose a part of the parent class collection. I want changes from either location to be affect the other.
My real life situation is I am creating a part of an application that will record a database design. I have a ConstraintList collection inside of a Database class. The ConstraintList contains a Constraint class for each constraint in the database. I also have a TablesList collection in the Database class, that contains Table classes. In the Table class I have a ForeignKeyConstraintList where I want to expose the constraints from the parent (Database class) ConstraintList that are foreign key constraints for this Table class.
+-Database Class
|
+--ConstraintList <-----------
| |
+--TableList Same List
| |
+-Table Class |
| |
+-ForeignKeyConstraintList
I have tried using an existing List class from the primary collection and using Linq to filter it to another List collection. However this doesn't work because this makes two List classes. If an entry is removed from the one List it still exists in the other List.
I thought about having the ForeignKeyConstraintList property of the Table class pull directly from the ConstraintList property of the Database class each time it is called but the act of filtering it causes it to create a new List class and thus any entries removed from ForeignKeyConstraintList would not be removed from the ConstraintList.
Another option I came up with so far is creating a new class that implements the same interfaces as List but doesn't subclass from it. Then using a private field to store a reference to the primary List class. Then writing custom Add and Remove methods that sync any changes back to the ConstraintList. I would also need to create a custom implementation of the IEnemerable and IEnumerable to skip items that don't meet the filter criteria.
In a parent class, I have a collection. In a child class, I want to expose a part of the parent class collection. I want changes from either location to be affect the other.
I decided to write a custom List type class to accomplish this. I will post the code below. I haven't tested yet but I figured this would be a good start for anyone else who wants to do the same thing.
hmmmm, seems the class is too large to fit in here. I will just post the key parts and skip the public methods, which just implement the various interfaces.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace CodeWriter.Collections.Generic
{
/// <summary>
/// This represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort and manipulate the list.
/// This class serves as a wrapper for a <see cref="List{T}"/>. The internal class can be reached by the <see cref="SourceList"/> property.
/// The elements that this class exposes from the <see cref="SourceList"/> can be controlled by changing the <see cref="Filter"/> property.
/// </summary>
/// <typeparam name="T">The type of elements in the list.</typeparam>
/// <remarks>
/// This class was created to support situations where the functionality of two or more <see cref="List{T}"/> collections are needed where one is the Master Collection
/// and the others are Partial Collections. The Master Collection is a <see cref="List{T}"/> and exposes all elements in the collection. The Partial Collections
/// are <see cref="FilteredList{T}"/> classes (this class) and only expose the elements chosen by the <see cref="FilteredList{T}"/> property of this class. When elements are modified,
/// in either type of collection, the changes show up in the other collections because in the backend they are the same list. When elements are added or deleted from the Partial Collections,
/// they will disappear from the Master Collection. When elements are deleted from the Master Collection, they will not be available in the Partial Collection but it
/// may not be apparent because the <see cref="Filter"/> property may not be exposing them.
/// </remarks>
public class FilteredList<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>
{
#region Public Constructor
public FilteredList(List<T> SourceList)
{
if (SourceList == null)
{
throw new ArgumentNullException("SourceList");
}
_SourceList = SourceList;
}
public FilteredList()
{
_SourceList = new List<T>();
}
public FilteredList(IEnumerable<T> Collection)
{
if (Collection == null)
{
throw new ArgumentNullException("Collection");
}
_SourceList = new List<T>(Collection);
}
#endregion
#region Protected Members
protected List<T> _SourceList;
protected Func<T, bool> _Filter;
#endregion
#region Public Properties
#region Source List Properties
/// <summary>
/// Gets or sets the base class that this class is a wrapper around.
/// </summary>
public List<T> SourceList
{
get
{
return _SourceList;
}
set
{
_SourceList = value;
}
}
/// <summary>
/// Gets or sets the value used to filter the <see cref="SourceList"/>.
/// </summary>
public Func<T, bool> Filter
{
get
{
return _Filter;
}
set
{
_Filter = value;
}
}
#endregion
#region Normal List<T> Implementation
/// <summary>
/// Provides access to the collection the in the same manner as an <see cref="Array"/>.
/// </summary>
/// <param name="Index">The Index of the element you want to retrieve. Valid values are from zero to the value in the <see cref="Count"/> property.</param>
/// <returns>The element at the position provided with the indexer.</returns>
public T this[int Index]
{
get
{
List<T> Selected = _SourceList.Where(_Filter).ToList();
return Selected[Index];
}
set
{
List<T> Selected = _SourceList.Where(_Filter).ToList();
Selected[Index] = value;
}
}
/// <summary>
/// Provides access to the collection the in the same manner as an <see cref="Array"/>.
/// </summary>
/// <param name="Index">The Index of the element you want to retrieve. Valid values are from zero to the value in the <see cref="Count"/> property.</param>
/// <returns>The element at the position provided with the indexer.</returns>
/// <remarks>This is required for IList implementation.</remarks>
object IList.this[int Index]
{
get
{
return this[Index];
}
set
{
if ((value is T) == false)
{
throw new ArgumentException("Value passed is not a valid type.");
}
this[Index] = (T)value;
}
}
/// <summary>
/// Gets or sets the total number of elements the internal data structure can hold without resizing.
/// </summary>
public int Capacity
{
get
{
return _SourceList.Capacity;
}
set
{
// We cannot let them shrink capacity because this class is a wrapper for the List<T> in the _SourceList property.
// They don't get to see all the entries in that list because it is filtered. Therefore it is not safe for them to shrink capacity.
// We check if they are shrinking the capacity.
if (value >= _SourceList.Capacity)
{
_SourceList.Capacity = value;
}
}
}
/// <summary>
/// Gets the number of elements contained in the <see cref="FilteredList{T}"/>.
/// </summary>
public int Count
{
get
{
List<T> Selected = _SourceList.Where(_Filter).ToList();
return Selected.Count();
}
}
/// <summary>
/// Gets a value indicating whether the <see cref="FilteredList{T}"/> has a fixed size.
/// </summary>
public bool IsFixedSize
{
get
{
return false;
}
}
/// <summary>
/// Gets a value indicating whether the <see cref="FilteredList{T}"/> is read-only.
/// </summary>
public bool IsReadOnly
{
get
{
return false;
}
}
/// <summary>
/// Gets a value indicating whether access to the <see cref="FilteredList{T}"/> is synchronized (thread safe).
/// </summary>
public bool IsSynchronized
{
get
{
return false;
}
}
/// <summary>
/// Gets an object that can be used to synchronize access to the <see cref="FilteredList{T}"/>.
/// </summary>
public object SyncRoot
{
get
{
return _SourceList;
}
}
#endregion
#endregion
}
}

Avoid non-readonly static fields - Immutability NDepend

I am using NDepend for code analysis and got this warning:
https://www.ndepend.com/default-rules/NDepend-Rules-Explorer.html?ruleid=ND1901#!
This rule warns about static fields that are not declared as read-only.
In Object-Oriented-Programming the natural artifact to hold states that can be modified is instance fields. Such mutable static fields create confusion about the expected state at runtime and impairs the code testability since the same mutable state is re-used for each test.
My code is as follows:
using Cosmonaut;
using Microsoft.Azure.Documents.Client;
using System.Configuration;
using LuloWebApi.Entities;
namespace LuloWebApi.Components
{
/// <summary>
/// Main class that encapsulates the creation of instances to connecto to Cosmos DB
/// </summary>
public sealed class CosmosStoreHolder
{
/// <summary>
/// Property to be initiated only once in the constructor (singleton)
/// </summary>
private static CosmosStoreHolder instance = null;
/// <summary>
/// To block multiple instance creation
/// </summary>
private static readonly object padlock = new object();
/// <summary>
/// CosmosStore object to get tenants information
/// </summary>
public Cosmonaut.ICosmosStore<SharepointTenant> CosmosStoreTenant { get; }
/// <summary>
/// CosmosStore object to get site collection information
/// </summary>
public Cosmonaut.ICosmosStore<SiteCollection> CosmosStoreSiteCollection { get; }
/// <summary>
/// CosmosStore object to get page templates information
/// </summary>
public Cosmonaut.ICosmosStore<PageTemplate> CosmosStorePageTemplate { get; }
/// <summary>
/// CosmosStore object to get pages information
/// </summary>
public Cosmonaut.ICosmosStore<Page> CosmosStorePage { get; }
/// <summary>
/// CosmosStore object to get roles information
/// </summary>
public Cosmonaut.ICosmosStore<Role> CosmosStoreRole { get; }
/// <summary>
/// CosmosStore object to get clients information
/// </summary>
public Cosmonaut.ICosmosStore<Client> CosmosStoreClient { get; }
/// <summary>
/// CosmosStore object to get users information
/// </summary>
public Cosmonaut.ICosmosStore<User> CosmosStoreUser { get; }
/// <summary>
/// CosmosStore object to get partners information
/// </summary>
public Cosmonaut.ICosmosStore<Partner> CosmosStorePartner { get; }
/// <summary>
/// CosmosStore object to get super administrators information
/// </summary>
public Cosmonaut.ICosmosStore<SuperAdministrator> CosmosStoreSuperAdministrator { get; }
/// <summary>
/// Constructor
/// </summary>
CosmosStoreHolder()
{
CosmosStoreSettings settings = new Cosmonaut.CosmosStoreSettings(ConfigurationManager.AppSettings["database"].ToString(),
ConfigurationManager.AppSettings["endpoint"].ToString(),
ConfigurationManager.AppSettings["authKey"].ToString());
settings.ConnectionPolicy = new ConnectionPolicy
{
ConnectionMode = ConnectionMode.Direct,
ConnectionProtocol = Protocol.Tcp
};
CosmosStoreTenant = new CosmosStore<SharepointTenant>(settings);
CosmosStoreSiteCollection = new CosmosStore<SiteCollection>(settings);
CosmosStorePageTemplate = new CosmosStore<PageTemplate>(settings);
CosmosStorePage = new CosmosStore<Page>(settings);
CosmosStoreRole = new CosmosStore<Role>(settings);
CosmosStoreClient = new CosmosStore<Client>(settings);
CosmosStoreUser = new CosmosStore<User>(settings);
CosmosStorePartner = new CosmosStore<Partner>(settings);
CosmosStoreSuperAdministrator = new CosmosStore<SuperAdministrator>(settings);
}
/// <summary>
/// Instance access, singleton
/// </summary>
public static CosmosStoreHolder Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new CosmosStoreHolder();
}
return instance;
}
}
}
}
}
However I am not sure how to fix this warning.
This is a guide, not a hard rule. Usually, non-readonly static fields are hard to intuit about. But in this case you're doing lazy deferred loading, so... a lock and mutate is indeed one way of achieving that, without causing it to be loaded prematurely.
So a pragmatic fix is: just ignore/override the warning
Another approach, however, is to move the field to another type where it is readonly, and rely on the deferred .cctor semantics:
public static CosmosStoreHolder Instance {
[MethodImpl(MethodImplOptions.NoInlining)]
get => DeferredHolder.Instance;
}
private static class DeferredHolder {
internal static readonly CosmosStoreHolder Instance = new CosmosStoreHolder();
}
Then you don't even need the lock semantics (.cctor deals with that for you).

NullReference When Attempting to Store in Variable's String [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 8 years ago.
I have two projects in my solution: HomeworkCalendar (VB.net Windows Forms Application) and HWLib (C# .dll Class Library). In the CurrentUser class in the library I have a variable defines as HWLib.User currentUser. This comes from the User class in HWLib:
namespace HWLib
{
public class User
{
/// <summary>
/// The Name of the User
/// </summary>
public String name = null;
/// <summary>
/// The Age of the User
/// </summary>
public int age = 0;
/// <summary>
/// The School Type of the User
/// </summary>
public String school = null;
/// <summary>
/// The Amount of Classes the User
/// </summary>
public int classesCount = 0;
/// <summary>
/// The String Array that holds all the classes
/// </summary>
public string[] classes;
}
}
Here is as it is in the CurrentUser class
public class CurrentUser
{
/// <summary>
/// The current User using the program
/// </summary>
public static HWLib.User currentUser;
}
So I attempted to store the user information into this variable, but this is where I get a NullReferenceException
Try
If intClasses <= 11 Then
CurrentUser.currentUser.name = txtName.Text
CurrentUser.currentUser.classesCount = intClasses
CurrentUser.currentUser.school = cboSchool.SelectedItem
CurrentUser.currentUser.age = Convert.ToInt32(cboAge.SelectedItem)
End if
Catch exx As NullReferenceException
'It does catch! This is the issue! Why does it catch here and how do I fix it?
File.Delete(Base.filePath)
MsgBox(exx.ToString())
End Try
Well, to get it to run you'll need to initialize currentUser:
public class CurrentUser
{
/// <summary>
/// The current User using the program
/// </summary>
public static HWLib.User currentUser = new HWLib.User() ;
}
but:
Why do you have a non-static class with just a static property? Just make CurrentUser static
It is a better practice to use properties (with getters/setters) instead of fields. THat allows you to add logic to the get/set without breaking client code.

Type.GetConstructor returns null on Iphone

I am developing an app in Xamarin for Iphone, android version of similar app is ready for Google play. I am using jabber-net library in my app for chat functionality. But having some issue on device(Iphone 5 - IOS 7.0.3). this issue doesn't occur in emulator following is the method code.
public class QnameType
{
/// <summary>
/// Element name
/// </summary>
protected internal string Name;
/// <summary>
/// Element namespace URI
/// </summary>
protected internal string NS;
/// <summary>
/// Type to create for NS/Name pair
/// </summary>
protected internal Type ElementType;
/// <summary>
/// Create a QnameType
/// </summary>
/// <param name="name"></param>
/// <param name="ns"></param>
/// <param name="typ"></param>
public QnameType(string name, string ns, Type typ)
{
this.Name = name;
this.NS = ns;
this.ElementType = typ;
}
/// <summary>
/// Is this the same qname by element name and namespace?
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
if (obj == (object)this)
return true;
QnameType other = obj as QnameType;
if (other == null)
return false;
return (other.Name == Name) && (other.NS == NS);
}
/// <summary>
/// Get a hash over the name and namespace.
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return ToString().GetHashCode();
}
/// <summary>
/// Namespace|Name
/// </summary>
/// <returns></returns>
public override string ToString()
{
return NS + "|" + Name;
}
}
public interface IPacketTypes
{
/// <summary>
/// QName to type mappings.
/// </summary>
QnameType[] Types { get; }
}
public class ElementFactory
{
private Hashtable m_types = new Hashtable();
private static readonly Type[] s_constructorTypes =
new Type[] { typeof(string),
typeof(XmlQualifiedName),
typeof(XmlDocument) };
public void AddType(IPacketTypes list)
{
foreach (QnameType qn in list.Types)
{
this.AddType(qn.Name, qn.NS, qn.ElementType);
}
}
public void AddType(string localName, string ns, Type t)
{
Debug.Assert(t.IsSubclassOf(typeof(Element)));
ConstructorInfo ci = t.GetConstructor(s_constructorTypes);
Debug.Assert(ci != null);
AddType(new XmlQualifiedName(localName, ns), ci);
}
public Element GetElement(string prefix, XmlQualifiedName qname, XmlDocument doc)
{
ConstructorInfo ci = (ConstructorInfo) m_types[qname];
if (ci == null)
{
return new Element(prefix, qname, doc);
}
return (Element) ci.Invoke
(new object[] {prefix, qname, doc});
}
/// <summary>
/// Get a constructor for the appropriate type for the given qname.
/// </summary>
public ConstructorInfo this[XmlQualifiedName qname]
{
get { return (ConstructorInfo) m_types[qname]; }
}
}
t.GetConstructor() returns null on Iphone but works fine on simulator.
Edit: added more detail,
Any help will be highly appreciated.
Thanks
That's likely normal, depending on t itself - what type does it represent ?
By default the managed linker is disabled (Don't link) on the simulator builds. That means every type will be part of the application.
However the default for device builds is Link SDK. This means unused types (found using static analysis) are removed from the application. This allow reducing the size of the application (by not compiling/shipping the whole .NET BCL inside each app).
Static analysis cannot find detect dynamic use of code, e.g. reflection. If your application depends on reflection is needs to preserve the code: using the [Preserve] attribute, an XML file or adding some extra code that will give an hint to the linker to keep the required members.
See documentation for further details.
In addition to the answer from poupou:
If you're stumbling over this question because you have a similar issue with Unity3D then take a look onto this page: https://docs.unity3d.com/Manual/IL2CPP-BytecodeStripping.html
In general that page shows you how to make sure everything is included in IL2CPP builds:
Use [Preserve] keyword on you classes
or
Add a link.xml file into your Assets root

Best way to implement generic setting class - Get/Set Properties reflected?

I don't know how I can make a generic settings class and hope that you can help me.
First of all I want a single settings file solution. For this I have created a Singleton like this:
public sealed class Settings
{
private static readonly Lazy<Settings> _instance = new Lazy<Settings>(() => new Settings());
private Dictionary<string, object> m_lProperties = new Dictionary<string, object>();
public void Load(string fileName)
{
throw new NotImplementedException();
}
public void Save(string fileName)
{
throw new NotImplementedException();
}
public void Update()
{
throw new NotImplementedException();
}
/// <summary>
/// Gets the propery.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <returns></returns>
public string GetPropery(string propertyName)
{
return m_lProperties[propertyName].ToString() ?? String.Empty;
}
/// <summary>
/// Gets the propery.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <param name="defaultValue">The default value.</param>
/// <returns></returns>
public string GetPropery(string propertyName, string defaultValue)
{
if (m_lProperties.ContainsKey(propertyName))
{
return m_lProperties[propertyName].ToString();
}
else
{
SetProperty(propertyName, defaultValue);
return defaultValue;
}
}
/// <summary>
/// Sets the property.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <param name="value">The value.</param>
public void SetProperty(string propertyName, string value)
{
if (m_lProperties.ContainsKey(propertyName))
m_lProperties[propertyName] = value;
else
m_lProperties.Add(propertyName, value);
}
}
But I think the better way is that the properties are in the classes and I can get the properties through reflection.
- Can you help me to implement something like this?
- Is it possible to give properties attributes like "encrypted = true"?
- Whats the best way to save / load the settings in a xml file?
Updated
Here is a example how to use the settings actual:
class Test()
{
private string applicationPath;
private string configurationPath;
private string configurationFile;
public Test()
{
applicationPath = Settings.Instance.GetPropery("ApplicationPath", AppDomain.CurrentDomain.BaseDirectory);
configurationPath = Settings.Instance.GetPropery("ConfigurationPath", "configurations");
configurationFile = Settings.Instance.GetPropery("ConfigurationFile", "application.xml");
// ... Load file with all settings from all classes
}
This here is a rather relevant bit from my own code.
public class MyObject
{
public string StringProperty {get; set;}
public int IntProperty {get; set;}
public object this[string PropertyName]
{
get
{
return GetType().GetProperty(PropertyName).GetGetMethod().Invoke(this, null);
}
set
{
GetType().GetProperty(PropertyName).GetSetMethod().Invoke(this, new object[] {value});
}
}
}
what it allows, is this:
MyObject X = new MyObject();
//Set
X["StringProperty"] = "The Answer Is: ";
X["IntProperty"] = 42;
//Get - Please note that object is the return type, so casting is required
int thingy1 = Convert.ToInt32(X["IntProperty"]);
string thingy2 = X["StringProperty"].ToString();
Updated: More Explanation
The way this works is to reflectively access properties, properties are different from fields in that they use getters and setters, as opposed to being directly declared and accessed. You can use this same method to get fields, or to also get fields, if you null check the return from GetProperty instead of simply assuming it works. Also, as was pointed out in another comment, this will break if you call it as is with a property that doesn't exist, because it lacks any form of error catching. I showed the code in its simplest possible form, not its most robust form.
As far as property attributes....that indexer needs to be created inside the class you want to use it with (or a parent class, I have it on my BaseObject), so internally you can implement attributes on given properties and then apply switches or checks against the properties when they are accessed. Maybe make all the properties some other custom class where you implement Object Value; Bool Encrypted; then work on it as needed from there, it really just depends on how fancy you want to get and how much code you want to write.
I not reccommend use Reflection in places where it possible do without it, as it very slow.
My example without reflection and Encryption prototype:
public sealed class Settings
{
private static readonly HashSet<string> _propertiesForEncrypt = new HashSet<string>(new string[] { "StringProperty", "Password" });
private static readonly Lazy<Settings> _instance = new Lazy<Settings>(() => new Settings());
private Dictionary<string, object> m_lProperties = new Dictionary<string, object>();
public void Load(string fileName)
{
// TODO: When you deserialize property which contains into "_propertiesForEncrypt" than Decrypt this property.
throw new NotImplementedException();
}
public void Save(string fileName)
{
// TODO: When you serialize property which contains into "_propertiesForEncrypt" than Encrypt this property.
throw new NotImplementedException();
}
public void Update()
{
throw new NotImplementedException();
}
/// <summary>
/// Gets the propery.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <returns></returns>
public object GetPropery(string propertyName)
{
if (m_lProperties.ContainsKey(propertyName))
return m_lProperties[propertyName];
return null;
}
/// <summary>
/// Gets the propery.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <param name="defaultValue">The default value.</param>
/// <returns></returns>
public object GetPropery(string propertyName, object defaultValue)
{
if (m_lProperties.ContainsKey(propertyName))
{
return m_lProperties[propertyName].ToString();
}
else
{
SetProperty(propertyName, defaultValue);
return defaultValue;
}
}
/// <summary>
/// Sets the property.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <param name="value">The value.</param>
public void SetProperty(string propertyName, object value)
{
if (m_lProperties.ContainsKey(propertyName))
m_lProperties[propertyName] = value;
else
m_lProperties.Add(propertyName, value);
}
// Sample of string property
public string StringProperty
{
get
{
return GetPropery("StringProperty") as string;
}
set
{
SetProperty("StringProperty", value);
}
}
// Sample of int property
public int IntProperty
{
get
{
object intValue = GetPropery("IntProperty");
if (intValue == null)
return 0; // Default value for this property.
return (int)intValue;
}
set
{
SetProperty("IntProperty", value);
}
}
}
Use a dynamic class like this:https://gist.github.com/3914644 so you could access your properties as: yourObject.stringProperty or yourObject.intProperty
One of the biggest issues is that there is no clean way to de-serialize an Object into an Object. if you dont know ahead of time what the Type of the object needs to be, its very hard to work with. So we have an alternate solution, store the type information.
Given that its not listed, I will provide what I consider an example XML, as well as a method of using it, and a method of accessing the properties themselves. The functions you are using for Get and Set properties are functional as is, and require no changes.
In the individual classes, you need to make sure the relevant properties in that class reference the Settings class in their own get/set methods
public int? MyClassProperty
{
get
{
return (int?)Settings.Instance.GetProperty("MyClassProperty");
}
set
{
Settings.Instance.SetProperty("MyClassProperty", value);
}
}
In your load and save functions, you will want to use Serialization, specifically, XmlSerializer. To do this, you need to declare your list of settings appropriately. For this I would actually use a custom class.
Updated to allow proper loading
public class AppSetting
{
[XmlAttribute("Name")]
public string Name { get; set; }
[XmlAttribute("pType")]
public string pType{ get; set; }
[XmlIgnore()]
public object Value{ get; set; }
[XmlText()]
public string AttributeValue
{
get { return Value.ToString(); }
set {
//this is where you have to have a MESSY type switch
switch(pType)
{ case "System.String": Value = value; break;
//not showing the whole thing, you get the idea
}
}
}
Then, instead of just a dictionary, you would have something like:
public sealed class Settings
{
private static readonly Lazy<Settings> _instance = new Lazy<Settings>(() => new Settings());
private Dictionary<string, object> m_lProperties = new Dictionary<string, object>();
private List<AppSetting> mySettings = new List<AppSetting>();
your load function would be a simple de-serialize
public void Load(string fileName)
{//Note: the assumption is that the app settings XML will be defined BEFORE this is called, and be under the same name every time.
XmlSerializer ser = new XmlSerializer(typeof(List<AppSetting>));
FileStream fs = File.Open(fileName);
StreamReader sr = new StreamReader(fs);
mySettings = (List<AppSetting>)ser.DeSerialize(sr);
sr.Close();
fs.Close();
//skipping the foreach loop that will add all the properties to the dictionary
}
the save function would essentially need to reverse it.
public void Save(string fileName)
{
//skipping the foreach loop that re-builds the List from the Dictionary
//Note: make sure when each AppSetting is created, you also set the pType field...use Value.GetType().ToString()
XmlSerializer ser = new XmlSerializer(typeof(List<AppSetting>));
FileStream fs = File.Open(fileName, FileMode.Create);
StreamWriter sw = new StreamWriter(fs);
//get rid of those pesky default namespaces
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
ser.Serialize(sw, mySettings, ns);
sw.Flush();
sw.Close();
fs.Close();
mySettings = null;//no need to keep it around
}
and the xml would resemble something like this:
updated
<ArrayOfAppSetting>
<AppSetting Name="ApplicationPath" pType="System.String">C:\Users\ME\Documents\Visual Studio 2010\Projects\WindowsFormsApplication1\WindowsFormsApplication1\bin\Debug\</AppSetting>
<AppSetting Name="ConfigurationPath" pType="System.String">configurations</AppSetting>
<AppSetting Name="ConfigurationFile" pType="System.String">application.xml</AppSetting>
<AppSetting Name="prop" pType="System.Int32">1</AppSetting>
</ArrayOfAppSetting>
I showed this example using the intermediate List<> because as it turns out you can't use anything that implements IDictionary with XmlSerializer. It will fail to initialize, it just doesn't work.
You can either create and maintain the list alongside the dictionary, or you can replace the dictionary with the List...make sure you have checks to verify that "Name" is unique, or you can simply ignore the list except during the Save and Load operations (which is how I wrote this example)
Update
This really only works well with Primitive types (int, double, string, etc..), but because you directly store the type, you can use any custom type you want, because you know what it is and what to do with it, you just have to handle it in the set method of AttributeValue
Another Update: If you are only storing strings, instead of objects of all types...it gets ridiculously simpler. get rid of the XmlIgnore value AND the pType, then auto-implement AttributeValue. Boom, done. That will limit you to strings and other primitives though, make sure that the Get/Set for the values in other classes cast them appropriately...but it is a much simpler and easier implementation.

Categories

Resources