Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set property value through an attribute tag name using reflection?

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.

like image 720
Will Marcouiller Avatar asked Mar 03 '10 17:03

Will Marcouiller


People also ask

How does reflection set property value?

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.

How do I set property value?

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.

How to get set a value in property in C#?

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.

What are attributes and reflections in C#?

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.


1 Answers

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.

like image 88
Andrew Bezzub Avatar answered Oct 21 '22 09:10

Andrew Bezzub