So here's my class from before but now with attributes:
[DirectorySchema("CN=Users,CN=Employees,DC=Northwind,DC=local", "Person")] public class User { [DirectoryAttribute] public string DistinguishedName { get; set; } [DirectoryAttribute("badpwdcount")] public int BadPasswordCount { get; set; } [DirectoryAttribute("directreports")] public string[] Employees { get; set; } [DirectoryAttribute] public string Title { get; set; } [DirectoryAttribute] public string PostalCode { get; set; } [DirectoryAttribute("cn")] public string CommonName { get; set; } [DirectoryAttribute] public DateTime? WhenCreated { get; set; } [DirectoryAttribute("givenname")] public string FirstName { get; set; } [DirectoryAttribute("objectguid")] public Guid Guid { get; set; } [DirectoryAttribute("l")] public string City { get; set; } [DirectoryAttribute("usnchanged")] public int Version { get; set; } [DirectoryAttribute("c")] public string Country { get; set; } [DirectoryAttribute("whenchanged")] public DateTime? LastChanged { get; set; } [DirectoryAttribute("objectsid")] public byte[] Sid { get; set; } [DirectoryAttribute("employeeid")] public long EmployeeId { get; set; } [DirectoryAttribute("telephonenumber")] public string PhoneNumber { get; set; } [DirectoryAttribute] public string Street { get; set; } [DirectoryAttribute] public string Comment { get; set; } [DirectoryAttribute] public string Name { get; set; } [DirectoryAttribute("sn")] public string LastName { get; set; } }
And here's how I added support:
public static bool HasDirectorySchema(this Type type) { var attributes = type.GetCustomAttributes(typeof (DirectorySchemaAttribute), true); return attributes != null && attributes.Length > 0; } internal static IObjectMapping Map<T>(string namingContext, string objectCategory) where T : class { lock (Mappings) { var mapping = GetMapping<T>() ?? (typeof (T).HasDirectorySchema() ? Map(new AttributeClassMap<T>()) : Map(new AutoClassMap<T>(namingContext, objectCategory))); return mapping; } } public class AttributeClassMap<T> : ClassMap<T> where T : class { public AttributeClassMap() { var type = typeof(T); var schemaAttribute = type .GetCustomAttributes(typeof(DirectorySchemaAttribute), true) .Cast<DirectorySchemaAttribute>().First(); NamingContext(schemaAttribute.NamingContext); ObjectCategory(schemaAttribute.ObjectCategory); var properties = type.GetProperties(Flags) .Where(p => p.GetCustomAttributes(typeof(DirectoryAttributeAttribute), true).Any() && p.GetGetMethod() != null && p.GetSetMethod() != null) .Select(p => new KeyValuePair<DirectoryAttributeAttribute, PropertyInfo>( p.GetCustomAttributes(typeof (DirectoryAttributeAttribute), true) .Cast<DirectoryAttributeAttribute>().FirstOrDefault(), p)); properties.ToList().ForEach(p => Map(p.Value).Named(p.Key.AttributeName)); } }
Attribute mapping is kind of the bridge between auto-mapping and class mapping. You can query like with class mapping, but without creating a separate class that maps your properties.
using (var context = new DirectoryContext()) { var user = context.Query<User>() .FirstOrDefault(u => u.CommonName == "Alan Hatter"); }
This comment has been removed by the author.
ReplyDeleteCan you do "Contains" queries? Can you create a method that allows you to pass in a Filter string?
ReplyDeleteTim,
ReplyDelete1. Yes, Contains works and builds an or filter against all the contents of the collection/array.
2. Right now there's no way to pass in a filter yourself, but I do have class for building equal or not equal filters:
u => Filter.Equal(u, "attribute name", "attribute value")
This will build an equal filter for whatever attribute name and value you used so you can still query against attributes that are not mapped.
Tim,
ReplyDeleteI just checked in a way to specify your own filters. I created an extension method called FilterWith (I'll probably end up changing the name) that allows you to pass in your own filter so you can do something like this:
context.Query().FilterWith("(SAmAccountName=ahatter)").ToList();
I hope this is what you were thinking.
That's awesome. Basically I'm running into situations where I need to add AND or OR filters dynamically.
ReplyDeleteKind of gets me stuck with building my own query string.
I've been in that boat before. I was glad to be able to add FilterWith. I also support dynamic expressions using two methods I got from Predicate Builder. I'll cover them in my upcoming Query post. I should have it up tomorrow or Saturday.
ReplyDeleteThat's great! I just ran across the PredicateBuilder class, adding support for that to LinqToLdap makes this a pretty robust finshed product.
ReplyDeleteLooking forward to seeing documentation on it!