I recently had a request from one of my clients to redo some active directory tied classes so that they could be serialized to a distributed caching system. I found nearly zip regarrding this approach in docs and on the web, so I went about creating a test project to see what could be done.
The basic problem is this: each class involved must be marked with the [Serializable] attibute and viola, .NET does the rest. Unfortunately, you can't always do this. For instance, the following (highly simplified) class:
using System;
using System.DirectoryServices;
using System.Runtime.Serialization;
namespace SerializeTest
{
[Serializable]
public class Account
{
private DirectoryEntry _de;
private string _sDN = null;
public Account(string dn)
{
_sDN = dn;
//simplified - never hard code this stuff :)
_de = new DirectoryEntry(@"LDAP://demo.mycompany.com/" + _sDN);
}
public DirectoryEntry DirectoryEntry { get { return _de; } }
public string DN { get { return _sDN; } }
}
}
will generate runtime errors if you try to serialize it because System.DirectoryServices.DirectoryEntry is not marked as serializable. Well guess what? I can't mark that class, it's not mine. To solve this dilema, the member must be marked as non serializable, using the atttribute [NonSerialized]:
[NonSerialized] private DirectoryEntry _de;
That's well and good, but how do we get it back when we deserialize? To do this, we have to save the state in a standard variable, such as a string, and recreate the object when the class is deserialized.
This can be done by implementing the IDeserializationCallback interface. Implementing an interface looks very much like inheriting a class, so don't let the terminology scare you. We simply need to add to our class definition:
[Serializable]
public class Account : IDeserializationCallback
{ ... }
This interface requires you to implement a method called OnDeserialization and this is where we will "recreate" or DirectoryEntry object:
public virtual void OnDeserialization(Object sender)
{
_de = new DirectoryEntry(_sAdsiPath);
}
Since _sAdsiPath was serialized, we still have access to it upon deserialization, so we simply feed it back into our _de member variable and we're all set. The resultant code:
using System;
using System.DirectoryServices;
using System.Runtime.Serialization;
namespace SerializeTest
{
[Serializable]
public class Account : IDeserializationCallback
{
[NonSerialized] private DirectoryEntry _de;
private string _sAdsiPath = null;
private string _sDN = null;
public Account(string dn)
{
_sDN = dn;
_de = new DirectoryEntry(@"LDAP://demo.mycompany.com/" + _sDN);
_sAdsiPath = _de.Path;
}
public DirectoryEntry DirectoryEntry { get { return _de; } }
public string DN { get { return _sDN; } }
public virtual void OnDeserialization(Object sender)
{
_de = new DirectoryEntry(_sAdsiPath);
}
}
}
Happy Hacking*
Doug
*ethically of course...
Thursday, March 6, 2008
Subscribe to:
Posts (Atom)