A practical use of Serialization and Extension methods in C# 3.0


\Recently, reviewing a colleague’s design diagrams, I noticed that in order to keep the history of changes of a particular field, he had created a small table in SQL Server! For example, in a contract management program, we are asked to keep track of changes in Due Date and Contract Payments. A primitive way for doing this, as my friend had done, is to save changes in a table that has a SmallDateTime field name DueDate. A better way that I proposed him is to design a table like this:

Field Name

Type

ID

Bigint, Identity(1,1)

Key

Varchar(10)

XMLBody

NVarchar(8000)

In the above table, ID is the Primary key, Key is a string field used to group up objects and XMlBody is a text field (Char, Varchar, Text…) used to keep the history of changes! In order to save an object in this table, the only job for us is to serialize it! For example, we can serialize the objects to XML format (my preference).

How to serialize an object?

To serialize an object, it must be serialize able. Only instances of a class that has been marked with [Serializable] attribute are surely serialize able but, due to my experience, many simple objects can be serialized without this attribute!

For example suppose we are going to keep track of changes in a contract’s due date. The first step is to define a class like the following:

private class ContractChange

{

public DateTime DueDate { get; set; }

public DateTime ChangeDate { get; set; }

}

This class has not been marked with Serializable attribute but is serialized into XML properly. To serialize and desterilize objects we might create a generic class named SerializationHelper<T> :

private static class SerializationHelper<T> where T: class

{

public static string SerializeObject(T obj)

{

string XmlString = String.Empty;

try

{

MemoryStream MemStream = new MemoryStream();

XmlSerializer Serializer = new XmlSerializer(obj.GetType());

XmlTextWriter XmlText = new XmlTextWriter(MemStream, Encoding.Default);

Serializer.Serialize(XmlText, obj);

XmlString = Encoding.Default.GetString(MemStream.ToArray());

MemStream.Close();

}

catch

{

throw;

}

return XmlString;

}

public static T DeserializeObject(string xmlbody)

{

byte[] Buffer = Encoding.Default.GetBytes(xmlbody);

MemoryStream input = new MemoryStream(Buffer);

XmlTextWriter XMlWriter = new XmlTextWriter(input, Encoding.Default);

XmlSerializer Serialzier = new XmlSerializer(typeof(T));

return (T)Serialzier.Deserialize(input) ;

}

}

The above two methods are private because they are going to be used internally. The class will have the following methods in order to save/load objects:

public static class SerializationHelper<T> where T: class

{

private static string SerializeObject(T obj) …

private static T DeserializeObject(string xmlbody)…

public static void Save(T obj, string key)

{

string XmlBody = SerializeObject(obj);

// CODE FOR SAVING XMLBODY INTO DATABASE GOES HERE

}

public static List<T> Load(string Key)

{

// retreive all records from DB regarding to Key parameter.

// for each record, call DeserializeObject

return null; // replace null with deserialized object.

}

public static List<T> LoadByFilter(string Key, Func<T, bool> predicate)

{

return Load(Key).Where(predicate).ToList();

}

public static void DeleteKey(string Key)

{

// A code to delete all records whose Key field value is equal to

Key param.

}

}

Now we can save/load any type of information with this class and with a simple table. In an advanced condition, you might design a hierarchical structure for better organization of stored objects.

Anyway, the following code is a crude example of how to use this approach:

static void Main(string[] args)

{

ContractChange C = new ContractChange { ChangeDate = DateTime.Now ,

DueDate= DateTime.Parse(“2000/10/11″)

};

// storing

SerializationHelper<ContractChange>.Save(C, “DueDate”);

// retrieving

List<ContractChange> changes = SerializationHelper<ContractChange>.Load(“DueDate”);

}

As shown in the above code, we might save/load any kind of information only by defining a new class for it.

Using extension methods to extend this approach…

C# 3.0 introduces extension methods. Using extension methods we might add new methods to a class without deriving new classes. For example if a class can calculate sum of a series of numbers, we might add a method to it in order to calculate the average of them:

public class extended

{

public int sum()

{

return 7+3+2;

}

}

public static class extending

{

public static float average(this extended extnd)

{

return extnd.sum() / 3;

}

}

As you see, the class Extending is adding a method named average to class Extended. To get the average, you call average method, as it belongs to extended class:

extended ex = new extended();

Console.WriteLine(ex.average());

A complete description of how extended methods work is out of the scope of this article. Anyway, we may use this new C# feature to add Serialize methods to any object:

public static class SerializeExtender

{

public static string Serialize (this object obj)

{

string XmlString = String.Empty;

MemoryStream MemStream = new MemoryStream();

XmlSerializer Serializer = new XmlSerializer(obj.GetType());

XmlTextWriter XmlText = new XmlTextWriter(MemStream, Encoding.Default);

Serializer.Serialize(XmlText, obj);

XmlString = Encoding.Default.GetString(MemStream.ToArray());

MemStream.Close();

return XmlString;

}

}

To examine this code we may write a sample program like the one bellow:

class Program

{

static void Main(string[] args)

{

ContractChange C = new ContractChange { ChangeDate = DateTime.Now ,

DueDate= DateTime.Parse(“2000/10/11″)

};

Console.WriteLine(C.Serialize ());

Console.ReadKey();

}

}

Unfortunately extension methods can not be generic or can not belong to a generic class. Therefore, I  attached Serialize method to Object class but needless to say that not all classes are serialize able! Thus, calling this method on a non-serializable object will cause a run-time error.


About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s