locked
Getting object name instead of value from LDAP query RRS feed

  • Question

  • User36794367 posted
    After doing a search I located a post with a similar title as above and wanted to create a new post to aid in finding information. 

    Here is the offending code:

    <code>
        Sub getEmailList()
            sPath = "LDAP://my.domain.local"
                'Setup Datatable
            Dim table As New DataTable("Results")
            table.Columns.Add("Last Name")
            table.Columns.Add("First Name")
            table.Columns.Add("Email Address")
            Dim row As DataRow
            'Setup AD Query
            Dim de As New DirectoryServices.DirectoryEntry(sPath, "USERNAME", "PASSWORD")
            Dim dSearch As New DirectoryServices.DirectorySearcher(de, "(&(objectClass=user)(objectCategory=person))")
            dSearch.PropertiesToLoad.Add("sn")
            dSearch.PropertiesToLoad.Add("givenName")
            dSearch.PropertiesToLoad.Add("mail")
            'Loop and build Table

            Dim mySearchResult As DirectoryServices.SearchResult
            Dim mySearchResultColl As DirectoryServices.SearchResultCollection
            Dim myResultPropColl As DirectoryServices.ResultPropertyCollection
            Dim myResultPropValueColl As DirectoryServices.ResultPropertyValueCollection
            Dim propVal() As String
            Dim counter As Integer
            mySearchResultColl = dSearch.FindAll()
            For Each mySearchResult In mySearchResultColl
                    row = table.NewRow()
                    row("Last Name") = mySearchResult.Properties("sn")
                    row("First Name") = mySearchResult.Properties("givenName")
                    row("Email Address") = mySearchResult.Properties("mail")
                    table.Rows.Add(row)
            Next
                DataSet1.Tables.Add(table)
                DataView1.Table = DataSet1.Tables("Results")
                'DataView1.Sort = "Last Name"
                'Databind to dataset.
                DataGrid1.DataSource = DataSet1
                DataGrid1.DataBind()
        End Sub
    </code>

    When I run this, the DataGrid is populated, but all I see is: System.DirectoryServices.ResultPropertyValueCollection in the three columns.  When I change:
    <code>
    mySearchResult.Properties("sn")
    </code>
    to this:
    <code>
    mySearchResult.Properties("sn")(0).ToString()
    </code>

    I get the following error:

    System.NullReferenceException: Object reference not set to an instance of an object.

      I only want those three properties of the user to show up in the DataGrid and nothing else.  Am I getting this error because I need to loop through the Properties?  If so, how do I do this?  I have tried using a counter with a Do While, counter = counter +1 but get the same Object reference error as above. 

    Any assistance would be greatly appreciated.
    Wednesday, April 20, 2005 3:30 PM

All replies

  • User36794367 posted
    Anyone?  This is a current project so any help would be nice!
    Thursday, April 21, 2005 4:06 PM
  • User1354132231 posted
    You just need to check if the value exists first.  It would be something like this:

    if (sr.Properties.Contains("attribute))
    {
        //safe to access now
        sr.Properties["attribute"][0].ToString();
    }


    The 'null' reference occurs when the attribute is not on the object.  I would suggest defining the attributes you are after in a string array and then applying the pattern above in a loop as you iterate over the string array.  A full example of this that generically builds DataSets was available in an older post that looks to have been archived now.  I will include the code for it here so we can build the knowledge again.  Use your favorite translator for VB.NET:

    public DataSet FindUsers(string sFilter, string[] columns, string path, bool useCached)
    {
        //try to retrieve from cache first
        HttpContext context = HttpContext.Current;
        DataSet userDS = (DataSet)context.Cache[sFilter];
               
        if((userDS == null) || (!useCached))
        {
            //setup the searching entries
            DirectoryEntry deParent = new DirectoryEntry(path);
            //deParent.Username = Config.Settings.UserName;
            //deParent.Password = Config.Settings.Password;
            deParent.AuthenticationType = AuthenticationTypes.Secure;
               
            DirectorySearcher ds = new DirectorySearcher(deParent,sFilter,columns);
            ds.PageSize = 1000;
               
            using(deParent)
            {
                //setup the dataset that will store the results
                userDS = new DataSet("userDS");
                DataTable dt = userDS.Tables.Add("users");
                DataRow dr;
           
                //add each parameter as a column
                foreach(string prop in columns)
                {
                    dt.Columns.Add(prop, typeof(string));
                }

                using (SearchResultCollection src = ds.FindAll())
                {
                    foreach(SearchResult sr in src)
                    {
                        dr = dt.NewRow();
                        foreach(string prop in columns)
                        {
                            if(sr.Properties.Contains(prop))
                            {
                                dr[prop] = sr.Properties[prop][0];
                            }
                        }
                        dt.Rows.Add(dr);
                    }
                }
            }
            //cache it for later, with sliding 3 minute window
            context.Cache.Insert(sFilter, userDS, null, DateTime.MaxValue, TimeSpan.FromSeconds(180));
        }
        return userDS;
    }


    Now, just place a datagrid on your page, and with a few lines of code, you have a searcher:

    //sample use
    string qry = String.Format("(&(objectCategory=person)(givenName={0}*))", txtFirstName.Text);
    string[] columns = new string[]{"givenName", "sn", "cn", "sAMAccountName", "telephoneNumber", "l"}
    string ldapPath = "LDAP://dc=mydomain";

    DataSet ds = FindUsers(qry, columns, ldapPath, true);
    DataGrid1.DataSource = ds;
    DataGrid1.DataBind();


    If you were really clever, you would dynamically create the ldap query and just keep passing in a new one.  Alternatively, you could get some good performance wins by using a larger filter result set and caching the DataSet from the method (longer than 3 minutes hopefully), and then using the DataView.RowFilter to filter the cached DataSet before binding to DataGrid.  It would then never need to talk to AD again until you wanted to invalidate the cache.


    Thursday, April 21, 2005 6:45 PM
  • User36794367 posted
    Bleh that post turned out rather interesting.

    The goal is for a user to go to the web page and see the most uptodate information on the Email addresses listed in Active Directory.  So basically the page needs to iterate through all of the OUs and list out every single user's First Name, Last Name, and Email Address.  I guess I'll have to fish through the code you posted and figure out how to translate it into VB.NET using VS.NET2003.

    Thanks for the help I'll see what I can do.
    Thursday, April 21, 2005 6:50 PM
  • User1354132231 posted
    There are a number of translators that will convert that for you.  To do what you want using the FindUsers() method as I presented it, you would do this:

    //sample use
    string qry = "(&(objectCategory=person)(objectClass=user)"; //all users
    string[] columns = new string[]{"givenName", "sn", "mail"}; //first, last, email
    string ldapPath = "LDAP://dc=mydomain,dc=com"; //point to your root domain to get all users

    //hope you are using Paging in your DataGrid - this could be big!!
    DataSet ds = FindUsers(qry, columns, ldapPath, true);
    DataGrid1.DataSource = ds;
    DataGrid1.DataBind();


    Easy 'nuf.

    Thursday, April 21, 2005 7:16 PM
  • User36794367 posted
    dunnry,

    Thank you again for your post.  I was able to get the necessary information from AD thanks to your code.  I do have a couple more questions though:

    1.  At what point in the code can I sort the results by givenName?  I get the list with several blank entries where there is an sn, givenName, but no email address (none was entered in AD).  Can I sort these results by givenName so I can determine who has addresses and who doesn;t?

    2.  In your code you use            
                'deParent.Username = Config.Settings.UserName;
                'deParent.Password = Config.Settings.Password;
    I'm assuming this information is being pulled from the web.config file.  Do you perchance know the syntax inside the web.config file where I need to put this?

    Thanks!
    Wednesday, April 27, 2005 11:08 AM
  • User36794367 posted
    Got the answer to my first question already:

    Added the following:

     '**********************************************
    'Sort the list by givenName (First Name)
     'Other options are: sn, mail
    ds.Sort.PropertyName = "givenName"


    I also found the answer to another question.  It appears that with LDAP you can't tell the query to NOT include NULL values.  For example, if I query AD for all users, and get accounts that do not have a value for sn, I am unable to specify the following:

    !sn=null or \00

    You can only query for attributes that MUST have a value:

    sn=*

    The above will only return results where sn has a value.

    Wednesday, April 27, 2005 12:32 PM
  • User1005758432 posted

    There are a number of translators that will convert that for you.  To do what you want using the FindUsers() method as I presented it, you would do this:

    //sample use
    string qry = "(&(objectCategory=person)(objectClass=user)"; //all users
    string[] columns = new string[]{"givenName", "sn", "mail"}; //first, last, email
    string ldapPath = "LDAP://dc=mydomain,dc=com"; //point to your root domain to get all users

    //hope you are using Paging in your DataGrid - this could be big!!
    DataSet ds = FindUsers(qry, columns, ldapPath, true);
    DataGrid1.DataSource = ds;
    DataGrid1.DataBind();


    Easy 'nuf.

    Do I have to know exactly what the objectCategory, objectClass, givenName, sn, and mail to use the FindUsers? I tried it and I got this error.

    The (&(objectCategory=person)(objectClass=user) search filter is invalid.

    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

    Exception Details: System.ArgumentException: The (&(objectCategory=person)(objectClass=user) search filter is invalid.

    Source Error:

    Line 56:                 using (SearchResultCollection src = ds.FindAll())
    Line 57: {
    Line 58: foreach (SearchResult sr in src)
    Line 59: {
    Line 60: dr = dt.NewRow();
     

    If I do, how do I find those out? 

    Wednesday, October 11, 2006 3:45 PM
  • User1005758432 posted
    Any other idea? Help is still needed.
    Friday, October 13, 2006 8:42 AM
  • User653893081 posted

    i tried using your code but but iam getting an error

    Error 1 'FindUsers': member names cannot be the same as their enclosing type.

     What might be the problem??

    Friday, November 17, 2006 2:01 PM
  • User653893081 posted

    Iam new to asp.net and Iam trying to bring my LDAP values on to a grid view. When I run my application iam not able to see the grid (the page turns up empty) what could be the possible reason?? 

    <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p> </o:p>

    <o:p> </o:p>

    public DataSet Userlogin(string sFilter, string[] columns, string path, bool useCached)<o:p></o:p>

        {<o:p></o:p>

            // trying to retrieve data from cache<o:p></o:p>

            string ulid = Session["id"].ToString();<o:p></o:p>

            string password = Session["password"].ToString();<o:p></o:p>

            HttpContext context = HttpContext.Current;<o:p></o:p>

            DataSet usersDS = (DataSet)context.Cache[sFilter];<o:p></o:p>

            if ((usersDS == null) || (!useCached))<o:p></o:p>

            {<o:p></o:p>

                //setup searching entries<o:p></o:p>

                DirectoryEntry deParent = new DirectoryEntry(path, ulid, password);<o:p></o:p>

                deParent.AuthenticationType = AuthenticationTypes.Secure;<o:p></o:p>

                DirectorySearcher ds = new DirectorySearcher(deParent, sFilter, columns);<o:p></o:p>

                ds.PageSize = 1000;<o:p></o:p>

                using (deParent)<o:p></o:p>

                {<o:p></o:p>

                    //setup dataset that will store the results<o:p></o:p>

                    usersDS = new DataSet("userDS");<o:p></o:p>

                    DataTable dt = usersDS.Tables.Add("users");<o:p></o:p>

                    DataRow dr;<o:p></o:p>

                    // adding each property as a column<o:p></o:p>

                    foreach (string prop in columns)<o:p></o:p>

                    {<o:p></o:p>

                        dt.Columns.Add(prop, typeof(string));<o:p></o:p>

                    }<o:p></o:p>

                    using (SearchResultCollection src = ds.FindAll())<o:p></o:p>

                    {<o:p></o:p>

                        foreach (SearchResult sr in src)<o:p></o:p>

                        {<o:p></o:p>

                            dr = dt.NewRow();<o:p></o:p>

                            foreach (string prop in columns)<o:p></o:p>

                            {<o:p></o:p>

                                if (sr.Properties.Contains(prop))<o:p></o:p>

                                {<o:p></o:p>

                                    dr[prop] = sr.Properties[prop][0];<o:p></o:p>

                                }<o:p></o:p>

                            }<o:p></o:p>

                            dt.Rows.Add(dr);<o:p></o:p>

                        }<o:p></o:p>

    <o:p> </o:p>

                    }<o:p></o:p>

                }<o:p></o:p>

                // cache it for later, with 3 minutes sliding window<o:p></o:p>

                context.Cache.Insert(sFilter, usersDS, null, DateTime.MaxValue, TimeSpan.FromSeconds(180));<o:p></o:p>

            }<o:p></o:p>

            return usersDS;<o:p></o:p>

        }<o:p></o:p>

        protected void Page_Load(object sender, EventArgs e)<o:p></o:p>

        {<o:p></o:p>

       <o:p></o:p>

            lblulid.Text = Session["id"].ToString();<o:p></o:p>

            string username = Session["id"].ToString();<o:p></o:p>

            string query = "(|(cn=" + username + ") (userPrincipalName= " + username + "ad.ilstu.edu))";<o:p></o:p>

            string[] columns = new string[] { "givenname", "sn", "physicalDeliveryOfficeName", "telephoneNumber",  "mail", "cn" };<o:p></o:p>

            string ldapPath = "LDAP:// ";<o:p></o:p>

            GridView1.Visible = true;<o:p></o:p>

            DataSet ds = Userlogin(query, columns, ldapPath, true);<o:p></o:p>

            GridView1.DataSource = ds;<o:p></o:p>

            GridView1.DataBind();<o:p></o:p>

            <o:p></o:p>

        }<o:p></o:p>

    <o:p> </o:p>

    <o:p> </o:p>

        <asp:GridView ID="GridView1" runat="server" AllowPaging="True" AllowSorting="True"<o:p></o:p>

                AutoGenerateColumns="true" CellPadding="4" ForeColor="#333333" GridLines="None"<o:p></o:p>

                Style="z-index: 100; left: 161px; ; top: 211px" EnableViewState="true" ><o:p></o:p>

    Friday, November 17, 2006 4:52 PM