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