Tuesday, February 1, 2011

LINQ To LDAP: How Does It Work Part 2

So up until this point all of my posts have used the auto-mapping via anonymous types. For this post I want to cover manual mapping of classes.

Here is an example mapping:

public class User
{
    public string DistinguishedName { get; set; }
    public int BadPasswordCount { get; set; }
    public string[] Employees { get; set; }
    public string Title { get; set; }
    public string PostalCode { get; set; }
    public string CommonName { get; set; }
    public DateTime? WhenCreated { get; set; }
    public string FirstName { get; set; }
    public Guid Guid { get; set; }
    public string City { get; set; }
    public int Version { get; set; }
    public string Country { get; set; }
    public DateTime? LastChanged { get; set; }
    public byte[] Sid { get; set; }
    public long EmployeeId { get; set; }
    public string PhoneNumber { get; set; }
    public string Street { get; set; }
    public string Comment { get; set; }
    public string Name { get; set; }
    public string LastName { get; set; }
}

public class UserMapping : ClassMap<User>
{
    public UserMapping()
    {
        NamingContext("CN=Users,CN=Employees,DC=Northwind,DC=local");
        ObjectCategory("person");

        Map(u => u.BadPasswordCount).Named("badpwdcount");
        Map(u => u.Employees).Named("directreports");
        Map(u => u.Title);
        Map(u => u.PostalCode);
        Map(u => u.CommonName).Named("cn");
        Map(u => u.WhenCreated);
        Map(u => u.FirstName).Named("givenname");
        Map(u => u.Guid).Named("objectguid");
        Map(u => u.City).Named("l");
        Map(u => u.Version).Named("usnchanged");
        Map(u => u.Country).Named("c");
        Map(u => u.LastChanged).Named("whenchanged");
        Map(u => u.Sid).Named("objectsid");
        Map(u => u.DistinguishedName);
        Map(u => u.EmployeeId);
        Map(u => u.PhoneNumber).Named("telephonenumber");
        Map(u => u.Street);
        Map(u => u.Comment);
        Map(u => u.Name);
        Map(u => u.LastName).Named("sn");
    }
}

I specify the object's location in the directory by using NamingContext(). I also map it to a object category, but it's not required. Mapping properties is pretty straightforward. You use a lambda expression to map a property and if it has a different name in the directory then you can specify that with Named().

It's not always necessary to create a specific mapping for your classes. The same way auto-mapping works with anonymous types, you can use the class as an example and it will map everything by convention. You have two options if you want to auto-map an existing class:

//Option 1
var user = context.Query<User>("CN=Users,CN=Employees,DC=Northwind,DC=local")
                .FirstOrDefault(u => u.Name == "Alan Hatter");

//Option 2
var example = new User();
var user = context.Query(example, "CN=Users,CN=Employees,DC=Northwind,DC=local", "Person")
                .FirstOrDefault(u => u.Name == "Alan Hatter");

If you try to auto-map an existing class without specifying a namingContext, you will get a MappingException. Also, remember your property names have to match the names in the directory for everything to work with auto-mapping. I'll cover finding available attributes, their types, and names in my next post. Stay tuned!