Sunday, May 1, 2011

LINQ To LDAP: Dynamics! Part 2

In my previous post I talked about adding dynamics support. Now I want to cover how I did it. The ExpandoObject added in .Net 4.0 is the type that I return for all dynamic queries. The cool thing about this object is you can cast it to IDictionary<string, object> and do something like this:
dynamic user = context.Query("CN=Users,CN=Employees,DC=Northwind,DC=local")
    .Select("cn", "givenname", "sn", "telephonenumber")
    .FilterWith("cn=Andrew Fuller")
    .FirstOrDefault();

foreach (var attribute in (IDictionary<string, object>)user)
{
    Console.WriteLine(attribute.Key + ": " + attribute.Value);
}

This is especially nice if you prefer to work with static types rather than dynamic and for characters that are invalid for .Net property names but valid for attribute names (such as hyphens).

So other than that, here are a few things you should know about dynamic querying:
  • Dynamic mappings are never cached.
  • Selected attributes will always be present in the result with a value of null, even if the attribute does not exist in the directory.
  • All LINQ extension methods that are currently supported will work with dynamic querying as long as they don't require an expression (i.e. Where conditions)

Well that's about it. I'll end with the result transformer for dynamic objects:
internal class DynamicResultTransformer : IResultTransformer
{
    private readonly IEnumerable<string> _attributesToLoad;

    public DynamicResultTransformer(IEnumerable<string> attributesToLoad)
    {
        _attributesToLoad = attributesToLoad;
    }

    public object Transform(SearchResultEntry entry)
    {
        dynamic expando = new ExpandoObject();
        var attributes = expando as IDictionary<string, object>;

        var names = (_attributesToLoad == null || !_attributesToLoad.Any())
                        ? entry.Attributes.AttributeNames.Cast<string>()
                        : _attributesToLoad;
        foreach (var attribute in names)
        {
            var value = entry.Attributes[attribute];
            if (value == null || value.Count < 1)
            {
                attributes.Add(attribute, null);
            }
            else if (value.Count == 1)
            {
                attributes.Add(attribute, value[0]);
            }
            else if (value.Count > 1)
            {
                var type = value[0].GetType();
                attributes.Add(attribute, value.GetValues(type));
            }
        }

        return expando;
    }

    public object Default()
    {
        return default(ExpandoObject);
    }
}