Tuesday, December 21, 2010

LINQ to LDAP: It's Alive!

Basic query by example using auto-mapping

using (LdapConnection connection = new LdapConnection("localhost"))
{
    var example = new {DistinguishedName = "", Cn = ""};
    using (var context = new DirectoryContext(connection))
    {
        context.Query(example, 
                      "CN=Users,CN=Employees,DC=Northwind,DC=local")
               .FirstOrDefault(u => u.Cn == "Alan Hatter");
    }
}

using (LdapConnection connection = new LdapConnection("localhost"))
{
    var example = new { DistinguishedName = "", Cn = "" };
    using (var context = new DirectoryContext(connection))
    {
        context.Query(example, 
                      "CN=Users,CN=Employees,DC=Northwind,DC=local")
               .ToList();
    }
}

Sunday, December 5, 2010

LINQ To LDAP: string to int

Currently I'm working on the transform portion of Linq to LDAP. This is where you take what comes back from a directory search (SearchResponse with a collection of SearchResultEntry) and transform that into mapped objects. I currently have a class that allows me to cache property getters and setters as Action and Func delegates.

Why even go through the trouble of building custom logic for setters and getters? Well I learned reflection is pretty optimized up to about 1000 iterations. After that point it's performance is downhill from there. Also, it's normal for LDAP entries to have 50 to 100 attributes and calling set or get on all of the properties can get very slow.

I created a performance test that for 1000 iterations sets 30 string properties via reflection, standard setters, and my custom delegates.

Here are the numbers:
Raw Set: 7ms
Action Delegate: 17ms
Reflection: 46 ms

Not a huge difference in the scheme of things, but if a large result set comes back, I don't want to worry about transforming the objects being a bottleneck.

So now that that's out of the way, let's get to my real post. My original DelegateBuilder implementation looked like this:

public static Action<T, object> BuildSetter<T>(PropertyInfo propertyInfo)
{
    var instance = Expression.Parameter(typeof(T), "i");
    var argument = Expression.Parameter(typeof(object), "a");
    var setterCall = Expression.Call(
        instance,
        propertyInfo.GetSetMethod(),
        Expression.Convert(argument, propertyInfo.PropertyType));
    var setter = (Action<T, object>)Expression.Lambda(setterCall, instance, argument)
                                                    .Compile();
    return setter;
}

public static Func<T, object> BuildGetter<T>(PropertyInfo propertyInfo)
{
    var instance = Expression.Parameter(typeof(T), "i");
    var getterCall = Expression.Call(
        instance,
        propertyInfo.GetGetMethod());
    var conversion = Expression.Convert(getterCall, typeof(object));
    var getter = (Func<T, object>)Expression.Lambda(conversion, instance).Compile();
    return getter;
}

This was all fine and dandy until I wrote a unit test for my transform that looks like this:
[Test]
public void Transform_NonAnonymousTypeWithAllAttributesPresentInEntry_ReturnsMappedObjectWithAllPropertiesSet()
{
    //prepare
    var searchResultAttributesCollection =
        typeof (SearchResultAttributeCollection)
             .Create<SearchResultAttributeCollection>();
    searchResultAttributesCollection.CallMethod("Add",
                                                new object[]
                                                    {"Property1", new DirectoryAttribute("Property1", "prop1")});
    searchResultAttributesCollection.CallMethod("Add",
                                                new object[] { "Property2", new DirectoryAttribute("Property2", "2") });
    var searchResultsEntry = typeof(SearchResultEntry).Create<SearchResultEntry>(new object[]{"dn", searchResultAttributesCollection});
    var queriedProperties = new Dictionary<string, string>
                                {
                                    {"Property1", "Property1"},
                                    {"Property2", "Property2"}
                                };
            

    //act
    var instance = searchResultsEntry.Transform(queriedProperties, _mapping).Cast<SearchResultEntryExtensionsIntegrationTests>();

    //assert
    instance.Property1.Should().Be.EqualTo("prop1");
    instance.Property2.Should().Be.EqualTo(2);
}

See the second CallMethod that Adds "2" to the searchResultAttributesCollection? That has to be a string since Protocols will throw an exception if a value other than a string, uri, or byte[] is passed in. Well that breaks my BuildSetter since you can't do an explicit cast from a string to an int. You have to use int.Parse("2") to get the int value from the string. So how did I solve this? I'm not a fan of huge if blocks so I'll try to refactor this another time, but for now it works.

private static readonly Func<object, Type, object> ConvertStringToValueTypeIfNecessaryFunction =
    (o, t) =>
        {
            if (o is string && t.IsValueType)
            {
                if (t == typeof(short) || t == typeof(short?))
                {
                    return short.Parse(o as string);
                }
                if (t == typeof(int) || t == typeof(int?))
                {
                    return int.Parse(o as string);
                }
                if (t == typeof(long) || t == typeof(long?))
                {
                    return long.Parse(o as string);
                }
                if (t == typeof(double) || t == typeof(double?))
                {
                    return double.Parse(o as string);
                }
                if (t == typeof(float) || t == typeof(float?))
                {
                    return float.Parse(o as string);
                }
                if (t == typeof(decimal) || t == typeof(decimal?))
                {
                    return decimal.Parse(o as string);
                }
            }
            return o;
        };

public static Action<T, object> BuildSetter<T>(PropertyInfo propertyInfo)
{
    var instance = Expression.Parameter(typeof(T), "i");
    var argument = Expression.Parameter(typeof(object), "a");
    var type = Expression.Constant(propertyInfo.PropertyType, typeof(Type));

    var converterFunctionCall = Expression.Call(ConvertStringToValueTypeIfNecessaryFunction.Method, argument, type);
    var setterCall = Expression.Call(
        instance,
        propertyInfo.GetSetMethod(),
        Expression.Convert(converterFunctionCall, propertyInfo.PropertyType));
    var setter = (Action<T, object>)Expression.Lambda(setterCall, instance, argument)
                                                    .Compile();
    return setter;
}

Wednesday, November 24, 2010

LINQ To LDAP: Referential Integrity

I'm using the Northwind sample database to populate employees in Lightweight Directory Services.  I ran into an issue when trying to populate managers.  I incorrectly assumed that I could add employees in any order as long as I had a well formatted distinguished name for the manager.  Apparently some reference checking was being done behind the scenes for manager because I was getting a constraint error.  So a little refactoring and everything works!  Also, if no value exists for referenced entries, you can't just supply a null and call it good.  You have to opt out of sending that value or you'll get a null value error.

private void AddEmployee(LdapConnection connection, Employee employee)
{
    var distinguishedName = "CN=" + 
        employee.FirstName + " " + employee.LastName + 
        ",CN=Users,CN=Employees,DC=Northwind,DC=local";

    addedEmployees.Add(employee.EmployeeID, distinguishedName);

    string managerDistinguishedName = 
        employee.Manager == null 
            ? null
            : addedEmployees[employee.Manager.EmployeeID];

    List<DirectoryAttribute> attributes =
        new List<DirectoryAttribute>
            {
                new DirectoryAttribute("employeeID", employee.EmployeeID.ToString()),
                new DirectoryAttribute("sn", employee.LastName),
                new DirectoryAttribute("givenName", employee.FirstName),
                new DirectoryAttribute("comment", employee.Notes),
                new DirectoryAttribute("telephoneNumber", employee.HomePhone),
                new DirectoryAttribute("photo", employee.Photo),
                new DirectoryAttribute("title", employee.Title),
                new DirectoryAttribute("street", employee.Address),
                new DirectoryAttribute("l", employee.City),
                new DirectoryAttribute("c", employee.Country),
                new DirectoryAttribute("postalCode", employee.PostalCode)
            };
    if (!string.IsNullOrEmpty(managerDistinguishedName))
    {
        attributes.Add(
            new DirectoryAttribute("manager", managerDistinguishedName));
    }

    AddRequest addRequest = new AddRequest(distinguishedName, "user");
    addRequest.Attributes.AddRange(attributes.ToArray());
    connection.SendRequest(addRequest);

    if (employee.Employees.Any())
    {
        foreach (var subordinate in employee.Employees)
        {
            AddEmployee(connection, subordinate);
        }
    }
}

Tuesday, November 23, 2010

LINQ To LDAP

So big side step from my Mad Media Manager series.  Linq to LDAP has been an interest of mine since I found Bart De Smet's awesome Linq to AD.  I downloaded the source and started adding functionality that I thought should be there.  That worked pretty well for a while, but I eventually hit a wall when I wanted to add support for collection Contains operations so that I could do something like this
List<string> strings = new List<string>{"a", "b", "c"};
Expression<Func<MappedObject, bool> expression = 
     e => strings.Contains(e.SomeProperty);
and get a filter that does an “or equal” against the contents of the list.  There are workarounds to supporting this like creating the or condition dynamically, but I felt that I should be able to support it within the framework.  I’m aware that Bart is currently rewriting Linq to AD to support things like this, but I figured I’d take a stab at it myself and learn a lot about Linq and LDAP in the process.  So armed with Matt Warren's blog series I set out to write my own.

Sunday, August 22, 2010

Mad Media Manager: The Beginning

This is the first post in a series about an application I'm building on Codeplex called Mad Media Manager.  It's a WPF application with the goal of cataloging your software and movie ISOs and have one-click mounting capabilities to a virtual drive (Virtual Clone Drive is my virtual drive of choice, but others may be supported in the future).

I'm pretty new to this so the structure will most likely change, but the layout should look something like this:

  • Introduction
  • Choosing the Tools
  • The Domain
  • MVVM
  • Data Access
  • Application Services
This application is heavily influenced by Josh Smith's MVVM article, Ayende's NHibernate To-Do app article, and Jose Romaniello's posts.  There will also be a few things in there pulled from S#arp Architecture.  I'm pretty excited about this application since I'm primarily a web developer. It'll be interesting working in a stateful environment for a change.