I wish to write a reusable library for querying against AD with LDAP. I'm using both ActiveDs COM objects and System.DirectoryServices.
Greatly inspired by Bart de Smet LINQ to AD, I have written a SchemaAttribute and an DirectoryAttributeAttribute classes to use with a DirectorySource(Of T) class (Yes, it's VBNET, but any C# code will help as I'm fluent in both languages).
Now, when querying against AD using LDAP (System.DirectoryServices), you may choose what property/attribute you wish to get loaded by the DirectorySearcher class. Then, I've written myself a method that takes a ParramArray of String as its parameter, so that I add the LDAP properties to the DirectorySearcher.PropertiesToLoad() method within a foreach() statement. Here's a piece of code to make it clear (Assuming that ldapProps parameter will always contain value(s)):
Public Function GetUsers(ByVal ParamArray ldapProps() As String) As IList(Of IUser)
Dim users As IList(Of IUser) = New List(Of IUser)
Dim user As IUser
Dim de As DirectoryEntry = New DirectoryEntry(Environment.UserDomainName)
Dim ds As DirectorySearcher = New DirectorySearcher(de, "(objectClass=user)")
For Each p As String In ldapProps
ds.PropertiesToLoad(p)
Next
Try
Dim src As SearchResultCollection = ds.FindAll()
For Each sr As SearchResult In src
user = New User()
// This is where I'm stuck... Depending on the ldapProps required, I will fill only these in my User object.
Next
End Function
Here's a piece of my User class:
Friend NotInheritable Class User
Implements IUser
Private _accountName As String
Private _firstName As String
<DirectoryAttributeAttribute("SAMAccountName")> _
Public Property AccountName As String
Get
Return _accountName
End Get
Set (ByVal value As String)
If (String.Equals(_accountName, value)) Then Return
_accountName = value
End Set
End Property
<DirectoryAttributeAttribute("givenName")> _
Public Property FirstName As String
Get
Return _firstName
End Get
Set (ByVal value As String)
If (String.Equals(_firstName, value)) Then Return
_firstName = value
End Set
End Property
End Class
Now, I would like to benefit of those attributes that I put on top of my User class properties. I know how to get these attributes, and I know how to get my properties. What I am unsure is how to make sure that the right property will be set the right value from the SearchResult class to my User class.
EDIT As time plays against me, I can't wait to get acquainted with the concept of DirectorySource(Of T), as it requires a bit more coding for me to write to get it working. As a workaround, I'm writing a UserFactory class which will be called through my ActiveDirectoryFacade.
EDIT This SO question seems to be very close to what I wish to accomplish:
Reflection, Attributes and Property Selection
EDIT This looks like what I want: C# setting property values through reflection with attributes
Anyone has another idea or can confirm this is right?
I shall also mention that I'm stuck in .NET Framework 2.0 and VBNET2005. Otherwise, I would have used Bart de Smet's LINQ to AD library.
Thanks for any help.
To set property values via Reflection, you must use the Type. GetProperty() method, then invoke the PropertyInfo. SetValue() method. The default overload that we used accepts the object in which to set the property value, the value itself, and an object array, which in our example is null.
To set the value of an indexed property, call the SetValue(Object, Object, Object[]) overload. If the property type of this PropertyInfo object is a value type and value is null , the property will be set to the default value for that type.
Example explained It is a good practice to use the same name for both the property and the private field, but with an uppercase first letter. The get method returns the value of the variable name . The set method assigns a value to the name variable. The value keyword represents the value we assign to the property.
Attribute ReflectionReflection is a set of . NET APIs that facilitate retrieving metadata from C# attributes. Reflection is used to retrieve attributes associated with an attribute target. This code calls GetCustomAttributes to list the attribute type names for the Id property.
I'm not familiar with DirectoryServices, but if I got your question right you could use reflections to set the properties of your User object. To set the correct properties you should match names of the properties with data saved in the attributes on the properties of the User's object.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class DirectoryAttributeAttribute : Attribute
{
public DirectoryAttributeAttribute(string propertyName)
{
PropertyName = propertyName;
}
public string PropertyName
{
get; set;
}
}
public class User
{
[DirectoryAttributeAttribute("SAMAccountName")]
public string AccountName
{
get; set;
}
[DirectoryAttributeAttribute("givenName")]
public string FirstName
{
get; set;
}
}
// Finds property info by name.
public static PropertyInfo FindProperty(this Type type, string propertyName)
{
foreach (PropertyInfo propertyInfo in type.GetProperties())
{
object[] attributes = propertyInfo.GetCustomAttributes(typeof(DirectoryAttributeAttribute, false));
foreach (DirectoryAttributeAttribute attribute in attributes)
{
if (attribute.PropertyName == propertyName)
{
return propertyInfo;
}
}
}
return null;
}
SearchResult searchResult = ...;
if (searchResult != null)
{
User user = new User();
Type userType = typeof (User);
foreach (string propertyName in searchResult.Properties.PropertyNames)
{
// Find property using reflections.
PropertyInfo propertyInfo = userType.FindProperty(propertyName);
if (propertyInfo != null) // User object have property with DirectoryAttributeAttribute and matching name assigned.
{
// Set value using reflections.
propertyInfo.SetValue(user, searchResult.Properties[propertyName]);
}
}
}
If names of the properties you are going to fill can change you can store mapping of properties in Dictionary.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With