public sealed class DynamicSearchResultAttributeDictionary : DynamicObject, IDictionary<string, object> { private readonly SearchResultAttributeCollection _collection; private SearchResultAttributeDictionary _dictionary; public DynamicSearchResultAttributeDictionary(SearchResultAttributeCollection collection) { _collection = collection; } private SearchResultAttributeDictionary Dictionary { get { return _dictionary ?? (_dictionary = new SearchResultAttributeDictionary(_collection)); } } public override IEnumerable<string> GetDynamicMemberNames() { return Keys; } public override bool TryGetMember(GetMemberBinder binder, out object result) { Dictionary.TryGetValue(binder.Name, out result); return true; } //IDictionary<string, object> implementation down here } public class SearchResultAttributeDictionary : Dictionary<string, object> { public SearchResultAttributeDictionary(SearchResultAttributeCollection collection) : base(collection == null ? 0 : collection.Count, StringComparer.InvariantCultureIgnoreCase) { if (collection == null) return; foreach (var attribute in collection.AttributeNames.Cast<string>()) { var value = collection[attribute]; if (value.Count == 1) { Add(attribute, value[0]); } else if (value.Count > 1) { var type = value[0].GetType(); Add(attribute, value.GetValues(type)); } else { Add(attribute, null); } } } }
With this implementation I can provide dynamic members that are NOT case sensitive and will NOT throw an exception if they do not exist. I also implemented IDictionary<string, object> so you can still access it that way.
So here's the slightly newer hotness way to query:
IQueryable<dynamic> query = context .Query("CN=Users,CN=Employees,DC=Northwind,DC=local") .Select("cn", "givenname") .FilterWith("(cn=Andrew Fuller)"); //Dynamic dynamic user = query.FirstOrDefault(); Console.Write(user.cn) Console.Write(user.GIVENNAME) Console.Write(user["cn"]) Console.Write(user["GIVENNAME"]) //Typed Dictionary IDictionary<string, object> dictionary = query.FirstOrDefault(); Console.Write(dictionary["cn"]) Console.Write(dictionary["GIVENNAME"])
All in all, this was a pretty fun and relatively easy feature (no one asked for it, but it felt natural given the key-value nature of LDAP).
Now that this unexpected detour is out of the way, here's what's shaping up for version 1.5:
- New property maping support for date times with any format in the directory (file time or some other).
- Enum mapping (string or int)
- GetByDN and GetByCN methods to go to a specific distinguished name in the directory and load the attributes (faster if you know exactly what you're looking for since a Searchscope.Base is used).
- Dynamics of course!
I'll try to have this out next week but no promises. This project comes 100% from my spare time and I'm short on that lately.
No comments:
Post a Comment