.NET binary XML with pre-shared dictionary - c#

I'm using XmlDictionaryWriter to serialize objects to a database with data contract serializer.
It works great, both size and speed are 2 times better then using text/xml.
However, I'll have to deal with enormous count of records in my database, where any extra bytes are directly translated into the gigabytes of the DB size.
That's why I'd love to reduce the size further, by using an XML dictionary.
How do I do that?
I see that XmlDictionaryWriter.CreateBinaryWriter static method accepts the 2-nd parameter of type IXmlDictionary. The MSDN says "The XmlDictionary to use as the shared dictionary".
First I've tried to use the system-supplied implementation:
XmlDictionary dict = new XmlDictionary();
string[] dictEntries = new string[]
{
"http://schemas.datacontract.org/2004/07/MyContracts",
"http://www.w3.org/2001/XMLSchema-instance",
"MyElementName1",
"MyElementName2",
"MyElementName3",
};
foreach ( string s in dictEntries )
dict.Add( s );
The result is .NET framework completely ignores the dictionary, and still inserts the above strings as plain text instead of just referencing a corresponding dictionary entry.
Then I've created my own implementation of IXmlDictionary:
class MyDictionary : IXmlDictionary
{
Dictionary<int, string> values = new Dictionary<int, string>();
Dictionary<string, int> keys = new Dictionary<string, int>();
MyDictionary()
{
string[] dictEntries = new string[]
{
"http://schemas.datacontract.org/2004/07/MyContracts",
"http://www.w3.org/2001/XMLSchema-instance",
"MyElementName1",
"MyElementName2",
"MyElementName3",
};
foreach ( var s in dictEntries )
this.Add( s );
}
static IXmlDictionary s_instance = new MyDictionary();
public static IXmlDictionary instance { get { return s_instance; } }
void Add( string val )
{
if ( keys.ContainsKey( val ) )
return;
int id = values.Count + 1;
values.Add( id, val );
keys.Add( val, id );
}
bool IXmlDictionary.TryLookup( XmlDictionaryString value, out XmlDictionaryString result )
{
if ( value.Dictionary == this )
{
result = value;
return true;
}
return this.TryLookup( value.Value, out result );
}
bool IXmlDictionary.TryLookup( int key, out XmlDictionaryString result )
{
string res;
if ( !values.TryGetValue( key, out res ) )
{
result = null;
return false;
}
result = new XmlDictionaryString( this, res, key );
return true;
}
public bool /* IXmlDictionary. */ TryLookup( string value, out XmlDictionaryString result )
{
int key;
if ( !keys.TryGetValue( value, out key ) )
{
result = null;
return false;
}
result = new XmlDictionaryString( this, value, key );
return true;
}
}
The result is - my TryLookup methods are called OK, however DataContractSerializer.WriteObject produces an empty document.
How do I use a pre-shared dictionary?
Thanks in advance!
P.S. I don't want to mess with XmlBinaryReaderSession/XmlBinaryWriterSession: I don't have "sessions", instead I have a 10 GB+ database accessed by many threads at once. What I want is just static pre-defined dictionary.
Update: OK I've figured out that I just need to call "XmlDictionaryWriter.Flush". The only remaining question is - why doesn't the system-supplied IXmlDictionary implementation work as expected?

for the XmlDictionaryWriter you need to use session.
example:
private static Stream SerializeBinaryWithDictionary(Person person,DataContractSerializer serializer)
{
var stream = new MemoryStream();
var dictionary = new XmlDictionary();
var session = new XmlBinaryWriterSession();
var key = 0;
session.TryAdd(dictionary.Add("FirstName"), out key);
session.TryAdd(dictionary.Add("LastName"), out key);
session.TryAdd(dictionary.Add("Birthday"), out key);
session.TryAdd(dictionary.Add("Person"), out key);
session.TryAdd(dictionary.Add("http://www.friseton.com/Name/2010/06"),out key);
session.TryAdd(dictionary.Add("http://www.w3.org/2001/XMLSchema-instance"),out key);
var writer = XmlDictionaryWriter.CreateBinaryWriter(stream, dictionary, session);
serializer.WriteObject(writer, person);
writer.Flush();
return stream;
}

The only way I way able to replicate the issue with the IXmlDictionary not being used was when my class wasn't decorated with a DataContract attribute. The following app displays the difference in sizes with decorated and undecorated classes.
using System;
using System.Runtime.Serialization;
using System.Xml;
namespace XmlPresharedDictionary
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Serialized sizes");
Console.WriteLine("-------------------------");
TestSerialization<MyXmlClassUndecorated>("Undecorated: ");
TestSerialization<MyXmlClassDecorated>("Decorated: ");
Console.ReadLine();
}
private static void TestSerialization<T>(string lineComment) where T : new()
{
XmlDictionary xmlDict = new XmlDictionary();
xmlDict.Add("MyElementName1");
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream, xmlDict))
{
serializer.WriteObject(writer, new T());
writer.Flush();
Console.WriteLine(lineComment + stream.Length.ToString());
}
}
}
//[DataContract]
public class MyXmlClassUndecorated
{
public MyElementName1[] MyElementName1 { get; set; }
public MyXmlClassUndecorated()
{
MyElementName1 = new MyElementName1[] { new MyElementName1("A A A A A"), new MyElementName1("A A A A A") };
}
}
[DataContract]
public class MyXmlClassDecorated
{
public MyElementName1[] MyElementName1 { get; set; }
public MyXmlClassDecorated()
{
MyElementName1 = new MyElementName1[] { new MyElementName1("A A A A A"), new MyElementName1("A A A A A") };
}
}
[DataContract]
public class MyElementName1
{
[DataMember]
public string Value { get; set; }
public MyElementName1(string value) { Value = value; }
}
}

Related

Merge Sparse Data into Dictionary using Json.NET PopulateObject

I would like to load sparse data in JSON format to get a result with missing data filled in with defaults, but my defaults include predefined instances of an extensible set rather than just fixed fields.
For (arbitrary) example,
Types
class Link
{
public string Addr;
public short Port;
public Link() { Addr = "0.0.0.0"; Port = 80; }
public override string ToString() { return Addr + ":" + Port.ToString(); }
}
class Targets
{
public Link Fixed;
public Dictionary<string, Link> Variable;
public Targets()
{
Fixed = new Link() { Addr = "192.168.0.1" };
Variable = new Dictionary<string, Link>
{
["Common"] = new Link() { Addr = "192.168.0.2" }
};
}
public override string ToString()
{
var result = new System.Text.StringBuilder();
result.Append("Fixed").Append('=').Append(Fixed)
.Append(' ');
foreach (var link in Variable)
{
result.Append(link.Key).Append('=').Append(link.Value)
.Append(' ');
}
return result.ToString();
}
}
Usage
var targets = new Targets();
string json = #"{
'Fixed': { 'Port':12345 },
'Variable': {
'Common': { 'Port':12345 }
}
}";
Newtonsoft.Json.JsonConvert.PopulateObject(json, targets);
Console.WriteLine(targets);
Outputs Fixed=192.168.0.1:12345 Common=0.0.0.0:12345 rather than the desired Fixed=192.168.0.1:12345 Common=192.168.0.2:12345.
This shows that the desired merge logic works for fixed properties, but not for items in a Dictionary despite the fact that the Dictionary will otherwise serialize/deserialize just like a type with fixed properties.
Took me a while to figure this out. Json.NET has a dedicated function for merging two JObjects together. Here's your example modified to use this method:
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
namespace ConsoleApp3
{
class Link
{
public string Addr;
public short Port;
public Link() { Addr = "0.0.0.0"; Port = 80; }
public override string ToString() { return Addr + ":" + Port.ToString(); }
}
class Targets
{
public Link Fixed;
public Dictionary<string, Link> Variable;
public Targets()
{
Fixed = new Link() { Addr = "192.168.0.1" };
Variable = new Dictionary<string, Link>
{
["Common"] = new Link() { Addr = "192.168.0.2" },
["Common2"] = new Link() { Addr = "192.168.0.25" }
};
}
public override string ToString()
{
var result = new System.Text.StringBuilder();
result.Append("Fixed").Append('=').Append(Fixed)
.Append(' ');
foreach (var link in Variable)
{
if (link.Key != "Variable")
result.Append(link.Key).Append('=').Append(link.Value)
.Append(' ');
}
return result.ToString();
}
}
class Program
{
static void Main(string[] args)
{
var targets = new Targets();
JObject o1 = JObject.Parse( #"{
'Fixed': { 'Port':12345 },
'Variable': {
'Common': { 'Port':12345 }
}
}");
JObject o2 = JObject.FromObject(targets);
o2.Merge(o1, new JsonMergeSettings
{
// union array values together to avoid duplicates
MergeArrayHandling = MergeArrayHandling.Union
});
string json = o2.ToString();
Console.WriteLine(json);
JsonConvert.PopulateObject(json, targets);
Console.WriteLine(targets);
Console.ReadKey();
}
}
}
The output is:
{
"Fixed": {
"Addr": "192.168.0.1",
"Port": 12345
},
"Variable": {
"Common": {
"Addr": "192.168.0.2",
"Port": 12345
},
"Common2": {
"Addr": "192.168.0.25",
"Port": 80
}
}
}
Fixed=192.168.0.1:12345 Common=192.168.0.2:12345 Common2=192.168.0.25:80
EDIT by OP: Refined into extension methods without extra ToString/deserialization:
static class SerializerExtensions
{
public static T MergeObject<T>(this JsonSerializer serializer, JsonReader json, T target)
{
JObject o1 = JObject.FromObject(target, serializer);
JObject o2 = serializer.Deserialize(json) as JObject;
o1.Merge(o2, new JsonMergeSettings
{ // union array values together to avoid duplicates
MergeArrayHandling = MergeArrayHandling.Union,
// an explicit null removes an existing item
MergeNullValueHandling = MergeNullValueHandling.Merge,
});
serializer.Populate(o1.CreateReader(), target);
return target;
}
public static T MergeObject<T>(this JsonSerializer serializer, JsonReader json, JObject template)
{
JObject o1 = template.DeepClone() as JObject;
JObject o2 = serializer.Deserialize(json) as JObject;
o1.Merge(o2, new JsonMergeSettings
{ // union array values together to avoid duplicates
MergeArrayHandling = MergeArrayHandling.Union,
// an explicit null removes an existing item
MergeNullValueHandling = MergeNullValueHandling.Merge,
});
return serializer.Deserialize<T>(o1.CreateReader());
}
}

Failing to read XML content using XmlSerializer

I got this in my class:
namespace MSAToolsLibrary.PublisherEntry
{
[XmlRoot(ElementName = "PublisherDatabase", Namespace = "http://www.publictalksoftware.co.uk/msa")]
public class PublisherData
{
public PublisherData()
{
//_Publishers = new List<Publisher>();
_PublishersDictionary = new Dictionary<string, Publisher>();
}
public List<Publisher> Publishers
{
get { return _PublishersDictionary.Select(x => x.Value).ToList(); }
set { _PublishersDictionary = value.ToDictionary(x => x.Name, x => x); }
}
private Dictionary<string, Publisher> _PublishersDictionary;
[XmlIgnore]
public Dictionary<string, Publisher> PublisherDictionary
{
get { return _PublishersDictionary; }
}
public void AddPublisher(String strName, String strNotes, Gender eGender, Appointed eAppointedAs, Serving eServingAs, bool bUseForDemonstrations, bool bAvailableMidweek, bool bAvailableWeekend, DateTime[] listDatesNotAvailable)
{
Publisher _Publisher = new Publisher()
{
Name = strName,
Notes = strNotes,
Gender = eGender,
AppointedAs = eAppointedAs,
ServingAs = eServingAs,
};
_Publisher.Assignments.UseForDemonstrations = bUseForDemonstrations;
_Publisher.Availability.Midweek = bAvailableMidweek;
_Publisher.Availability.Weekend = bAvailableWeekend;
_Publisher.Availability.DatesNotAvailable = new List<DateTime>(listDatesNotAvailable);
//_Publishers.Add(_Publisher);
_PublishersDictionary.Add(strName, _Publisher);
}
}
}
Now, when I save my data to XML it all works good.
But when I read in:
public void ReadPublisherData(out Int64 iResult)
{
_PublisherData.Publishers.Clear(); // Reset
iResult = MakeResult(true);
try
{
XmlSerializer x = new XmlSerializer(_PublisherData.GetType());
using (StreamReader reader = new StreamReader(_strPathXML))
{
_PublisherData = (PublisherData)x.Deserialize(reader);
iResult = _PublisherData.PublisherDictionary.Count;
}
}
catch
{
iResult = MakeResult(false);
}
}
Doesn't work. I have zero publishers in the list or the dictionary.
What am I doing wrong?
Update
If I change the PublisherData declaration so that it has the needed back field:
public PublisherData()
{
_Publishers = new List<Publisher>();
_PublishersDictionary = new Dictionary<string, Publisher>();
}
public List<Publisher> Publishers
{
get => _Publishers; set => _Publishers = value;
}
private List<Publisher> _Publishers;
Then this causes the data to serialize correctly and I get what is expected in the MFC application. But now my PublisherDictionary is hanging. So I added a function:
public void BuildPublisherDictionary()
{
_PublishersDictionary = _Publishers.ToDictionary(x => x.Name, x => x);
}
And adjusted the read routine:
public void ReadPublisherData(out Int64 iResult)
{
iResult = MakeResult(true);
try
{
_PublisherData.Publishers.Clear(); // Reset
XmlSerializer x = new XmlSerializer(_PublisherData.GetType());
using (StreamReader reader = new StreamReader(_strPathXML))
{
_PublisherData = (PublisherData)x.Deserialize(reader);
_PublisherData.BuildPublisherDictionary();
iResult = _PublisherData.PublisherDictionary.Count;
}
}
catch
{
iResult = MakeResult(false);
}
}
It works. But I don't know if that is the simplest way. My problem with the above is that the list / dictionary are now detached from each other. So if I add a publisher to the dictionary it will now not be in the list.
Update 2
At the moment, after reading in the XML, if I add or remove a Publisher I am applying it to both the list and the dictionary. Unless there is a simpler way.
The serializer will only call the getter of your List and then call Add for each element it finds, providing it the instance of the type it deserialized from that element.
If your requirement is to have both a list and a dictionary you'll have to provide an implementation of an type that does so and implements an applicable interface.
The following ListDict uses a List and a Dictionary as their backing stores while implementing IList to be used in the (de)serializtion.
public class ListDict<K, T>:IList<T>
{
public Dictionary<K, T> dict = new Dictionary<K, T>();
public List<T> list = new List<T>();
Func<T,K> KeyFunc;
// takes an Function that returns a key for T
public ListDict(Func<T,K> keyfunc)
{
KeyFunc = keyfunc;
}
// called by the serializer
public void Add(T value)
{
Add( KeyFunc(value), value);
}
// fill both List and Dictionary
public void Add(K key, T value)
{
list.Add(value);
dict.Add( key , value);
}
// left out other required methods from IList<T>
}
Now your PublisherData class will change as follows to leverage above class:
[XmlRoot(ElementName = "PublisherDatabase", Namespace = "http://www.publictalksoftware.co.uk/msa")]
public class PublisherData
{
private ListDict<string, Publisher> _PublishersDictionary;
public PublisherData()
{
// provide the function to generate a key for a Publisher
_PublishersDictionary = new ListDict<string,Publisher>( (p) => p.Name );
}
[XmlElement]
public ListDict<string,Publisher> Publishers
{
get { return _PublishersDictionary; }
}
[XmlIgnore]
public Dictionary<string, Publisher> PublisherDictionary
{
get {return _PublishersDictionary.dict; }
}
}
Using above classes gives me a filled list and Dictionary directly after deserialization. You'll have to make sure of course to keep the backing stores in sync in the ListDict. Maybe you can do without it but that depends on your exact usecase.

Error serializing MultiValueDictionary(string,string) with Protobuf-net

I am using MultiValueDictionary(String,string) in my project (C# - VS2012 - .net 4.5), which is a great help if you want to have multiple values for each key, but I can't serialize this object with protobuf.net.
I have serialized Dictionary(string,string) with Protobuf with ease and speed and MultiValueDictionary inherits from that generic type; so, logically there should be no problem serializing it with the same protocol.
Does any one know a workaround?
This is the error message when I execute my codes:
System.InvalidOperationException: Unable to resolve a suitable Add
method for System.Collections.Generic.IReadOnlyCollection
Do you realy need a dictionary?
If you have less than 10000 items in your dictionary you can also use a modified list of a datatype..
public class YourDataType
{
public string Key;
public string Value1;
public string Value2;
// add some various data here...
}
public class YourDataTypeCollection : List<YourDataType>
{
public YourDataType this[string key]
{
get
{
return this.FirstOrDefault(o => o.Key == key);
}
set
{
YourDataType old = this[key];
if (old != null)
{
int index = this.IndexOf(old);
this.RemoveAt(index);
this.Insert(index, value);
}
else
{
this.Add(old);
}
}
}
}
Use the list like this:
YourDataTypeCollection data = new YourDataTypeCollection();
// add some values like this:
data.Add(new YourDataType() { Key = "key", Value1 = "foo", Value2 = "bar" });
// or like this:
data["key2"] = new YourDataType() { Key = "key2", Value1 = "hello", Value2 = "world" };
// or implement your own method to adding data in the YourDataTypeCollection class
XmlSerializer xser = new XmlSerializer(typeof(YourDataTypeCollection));
// to export data
using (FileStream fs = File.Create("YourFile.xml"))
{
xser.Serialize(fs, data);
}
// to import data
using (FileStream fs = File.Open("YourFile.xml", FileMode.Open))
{
data = (YourDataTypeCollection)xser.Deserialize(fs);
}
string value1 = data["key"].Value1;

XML Deserialize items of generic types

Assume I have the following class:
public abstract class ScheduledService : ScheduledServiceBase<ScheduledService>
{
public CronInfo CronInfo;
public String ServiceName;
public ScheduledService()
{ }
}
public abstract class ScheduledServiceBase<T>
{
public ScheduledServiceBase()
{ }
public virtual void StartUp(IScheduler scheduler, ScheduledService service, Dictionary<string, object> parameters = null)
{
...
}
}
From this base class I create two inheriting classes like so:
public AlphaService : ScheduledService
{
public String Alpha_Name;
public Int32 Alpha_Age;
public AlphaService() { }
}
public BetaService : ScheduledService
{
public String Beta_Company;
public Boolean Beta_IsOpen;
public AlphaService() { }
}
Then in my XML I define the two Services:
<ScheduledServices>
<AlphaService>
<Alpha_Name>John Jones</Alpha_Name>
<Alpha_Age>32</Alpha_Age>
<ServiceName>FirstService</ServiceName>
<CronInfo>0 0 0/2 * * ? *</CronInfo>
</AlphaService>
<BetaService>
<Beta_Company>Ajax Inc.</Beta_Company>
<Beta_IsOpen>Ajax Inc.</Beta_IsOpen>
<ServiceName>SecondService</ServiceName>
<CronInfo>0 30 0/5 * * ? *</CronInfo>
</BetaService>
</ScheduledService>
When I deserialize this I am trying to assign the ScheduledServices to List<ScheduledService> ScheduledServices but nothing exists in the list after deserialization. The deserializer doesn't error out, it just doesn't create any services.
Is what I am trying to do valid or do I need to setup the services differently?
Here is what I am using to deserialize the XML:
public Boolean Load(params Type[] extraTypes)
{
deserializedObject = default(T);
XmlTextReader reader = null;
try
{
reader = new XmlTextReader(new StreamReader(_actualPath));
XmlSerializer serializer = new XmlSerializer(typeof(T), extraTypes);
deserializedObject = (T)(serializer.Deserialize(reader));
}
catch (Exception ex)
{
log.Error("Error: " + ex.Message);
log.Debug("Stack: " + ex.StackTrace);
log.Debug("InnerException: " + ex.InnerException.Message);
}
finally
{
if (reader != null) reader.Close();
}
return ((this.deserializedObject != null) && deserializedObject is T);
}
SOLUTION
Thanks to John Arlen and his example and link to another post I was able to do what I wanted by creating the list as follows:
[XmlArrayItem("AlphaJob", Type=typeof(AlphaJob))]
[XmlArrayItem("BetaJob", Type=typeof(BetaJob))]
public List<ScheduledService> ScheduledServices;
The XmlSerializer class needs more information than your XML is offering to work properly.
For example, it does not know exactly which Type is referred to by "AlphaService" ("MyNamespace.AlphaService, MyUtility"?)
The easiest way to start is create a sample object in memory and call XmlSerializer.Serialize(myObj) to understand the actual format it is looking for.
If you have no control over the incoming XML, you will either need to further decorate your classes (With XmlElementAttribute, for example), or use a different mechanism
EDIT: An example of marking up your class might be:
[XmlInclude( typeof( AlphaService ) )]
[XmlInclude( typeof( BetaService ) )]
public abstract class ScheduledService : ScheduledServiceBase<ScheduledService> {...}
And you can test this by serializing it to XML and back into a concrete object:
private void SerializeAndBack()
{
var item = new ScheduledServiceHost
{
ScheduledServices = new List<ScheduledService>
{
new AlphaService {Alpha_Age = 32, Alpha_Name = "John Jones", ServiceName = "FirstService"},
new BetaService {Beta_Company = "Ajax Inc.", Beta_IsOpen = true, ServiceName = "SecondService"},
}
};
var xmlText = XmlSerializeToString( item );
var newObj = XmlDeserializeFromString( xmlText, typeof( ScheduledServiceHost ) );
}
public static string XmlSerializeToString( object objectInstance, params Type[] extraTypes )
{
var sb = new StringBuilder();
using ( TextWriter writer = new StringWriter( sb ) )
{
var serializer = new XmlSerializer( objectInstance.GetType(), extraTypes );
serializer.Serialize( writer, objectInstance );
}
return sb.ToString();
}
public static object XmlDeserializeFromString( string objectData, Type type )
{
using ( var reader = new StringReader( objectData ) )
{
return new XmlSerializer( type ).Deserialize(reader);
}
}
Here is a good SO answer on some options for marking up a class is.

Refactoring functions to make more generic

I have the following code copy/pasted multiple times. The values that change are the string literals ("TabStates" changes to "ContentStates" etc..) and the value of the dictionary (RadTabSetting -> ContentSetting).
public static SerializableDictionary<string, RadTabSetting> GetTabStates()
{
SerializableDictionary<string, RadTabSetting> _tabStates = new SerializableDictionary<string, RadTabSetting>();
if (!object.Equals(DashboardSessionRepository.Instance.GetSession("TabStates"), null))
{
_tabStates = DashboardSessionRepository.Instance.GetSession("TabStates") as SerializableDictionary<string, RadTabSetting>;
}
else
{
XmlSerializer serializer = new XmlSerializer(_tabStates.GetType());
string data = DashoardDatabaseRepository.Instance.GetWebLayoutData("TabStates");
if ( !string.IsNullOrEmpty(data) )
{
byte[] dataAsArray = Convert.FromBase64String(data);
MemoryStream stream = new MemoryStream(dataAsArray);
_tabStates = serializer.Deserialize(stream) as SerializableDictionary<string, RadTabSetting>;
}
DashboardSessionRepository.Instance.SetSession("TabStates", _tabStates);
}
return _tabStates;
}
public static void SetTabStates(SerializableDictionary<string, RadTabSetting> tabStates)
{
DashboardSessionRepository.Instance.SetSession("TabStates", tabStates);
DashboardDatabaseRepository.Instance.SaveToDatabase("TabStates", tabStates);
}
I'm not looking for an answer, just curious what I should read about to learn how to rewrite this. I'm sure it's simple enough, just not sure what it's called. Is it just function templating?
public static T GetStates<T>() where T: new()
{
T _states = new T();//(T)Activator.CreateInstance(typeof(T));
string stateName = StateDictionary.GetStateName(typeof(T));
if (!object.Equals(DashboardSessionRepository.Instance.GetSession(stateName), null))
{
_states = (T)DashboardSessionRepository.Instance.GetSession(stateName);
//Work-Around
System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();
System.Xml.Serialization.XmlSerializer xmlSerializer = new System.Xml.Serialization.XmlSerializer(_states.GetType());
xmlSerializer.Serialize(memoryStream, _states);
string data = System.Convert.ToBase64String(memoryStream.ToArray());
string otherData = DashboardDatabaseRepository.Instance.GetWebLayoutData(stateName);
if (!string.IsNullOrEmpty(data))
{
XmlSerializer serializer = new XmlSerializer(_states.GetType());
byte[] dataAsArray = Convert.FromBase64String(data);
MemoryStream stream = new MemoryStream(dataAsArray);
_states = (T)serializer.Deserialize(stream);
}
//Work-Around
}
else
{
XmlSerializer serializer = new XmlSerializer(_states.GetType());
string data = DashboardDatabaseRepository.Instance.GetWebLayoutData(stateName);
if (!string.IsNullOrEmpty(data))
{
byte[] dataAsArray = Convert.FromBase64String(data);
MemoryStream stream = new MemoryStream(dataAsArray);
_states = (T)serializer.Deserialize(stream);
}
DashboardSessionRepository.Instance.SetSession(stateName, _states);
}
return _states;
}
public static void SetStates<T>(T states) where T: new()
{
string stateName = StateDictionary.GetStateName(typeof(T));
DashboardSessionRepository.Instance.SetSession(stateName, states);
DashboardDatabaseRepository.Instance.SaveToDatabase(stateName);
}
static class StateDictionary
{
//TODO: Might (should?) be able to redo this polymorphically.
static IDictionary<Type, string> _stateDictionary = new Dictionary<Type, string>
{
{typeof(SerializableDictionary<string, RadTabSetting>), "TabStates"},
{typeof(SerializableDictionary<string, RadDockContentSetting>), "ContentStates"},
{typeof(SerializableDictionary<string, RadPaneSetting>), "PaneStates"},
{typeof(SerializableDictionary<string, RadDockSetting>), "DockStates"},
{typeof(SerializableDictionary<string, RadDockZoneSetting>), "DockZoneStates"},
{typeof(SerializableDictionary<string, RadSplitterSetting>), "SplitterStates"},
{typeof(SerializableDictionary<string, RadSplitBarSetting>), "SplitBarStates"},
{typeof(KnownGlobalSettings), "GlobalSettings"},
};
public static string GetStateName(Type type)
{
string stateName = string.Empty;
if (_stateDictionary.ContainsKey(type))
{
stateName = _stateDictionary[type];
}
return stateName;
}
}
Martin Fowler's book is the standard source for refactoring.
But for your specific example, just create a new method that takes in parameters representing the things that change in your example. So you'll want a string parameter for your "TabStates" or "ContentStates" value, and another representing your Value for Key ContentSetting in your dictionary.
Does that make sense? Am I understanding your question fully?
EDIT
Based on your comment, you want to use generics. Something like this should get you going:
public static Dictionary<string, T> GetTabStates<T>()
Just note that you won't be able to do much with your type T unless you add some generic constraints.
If you want to create a new instance of T, then you would need
public static Dictionary<string, T> GetTabStates<T>() where T : new() {
And if you want to access actual properties on an instance of T, then hopefully all possible values for T will implement some sort of interface, in which case you would say:
public interface IFoo {
int Id { get; set; }
}
public static Dictionary<string, T> GetTabStates<T>() where T : new(), IFoo {
T Tval = new T();
Tval.Id = 1;
//etc

Categories

Resources