Friday, April 11, 2008

.NET Journal::Converting AD integer values into bitmapped enumerations

I have often found it useful to store information in bitmap enumerations e.g. [Flags] while programming in c#. Many of my projects have included storing persistent data in Active Directory or ADAM (Active Directory Application Mode) which requires conversion when getting data out of storage.

Here's a quick tidbit about the easiest and most abstractable way to approach such a task. In our example, we are storing an enumeration call "InheritanceModel ", defined as:

[Flags]
public enum InheritanceModel
{
Undefined = 0x0,
None =0x1,
Standard = 0x2,
Invertable = 0x4,
Overrideable = 0x8,
Blockable = 0x10
}

The [Flags] attribute is what allows us to manipulate the values using boolean logic (AND, OR, NOT, etc.). It is not particularly efficient to store such information as a converted string, so we store it as an integer. Our AD/ADAM ldap attribute is defined as an integer, we'll call it "InheritanceModel".

We can get and convert the integer value as follows:

InheritanceModel _InheritanceModel = InheritanceModel.Undefined;
DirectoryEntry _de = new DirectoryEntry(
LDAP://localhost:50000/CN=Folder,CN=....);

if (_de.Properties.Contains("InheritanceModel"))
{
foreach (int iValue in System.Enum.GetValues(typeof(InheritanceModel)))
{
// this shows how we can use the System.Enum functions to see the string value
string sInheritanceModel = System.Enum.GetName(typeof (InheritanceModel), iValue).ToString();
if ((iValue & (int)_de.Properties[("InheritanceModel"].Value) > 0)
{
_InheritanceModel = (InheritanceModel)System.Enum.ToObject(typeof (InheritanceModel), iValue);
}
}
}

Thursday, March 6, 2008

.NET Journal :: Serialization of DirectoryServices members

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...