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.
Ariston Collander
"No one person's opinions could be said to be 'truer' than another's. For each is the sole judge of his or her own experiences." - Protagoras
Anyone? This is a current project so any help would be nice!
Ariston Collander
"No one person's opinions could be said to be 'truer' than another's. For each is the sole judge of his or her own experiences." - Protagoras
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];
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:
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.
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.
Ariston Collander
"No one person's opinions could be said to be 'truer' than another's. For each is the sole judge of his or her own experiences." - Protagoras
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();
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!
Ariston Collander
"No one person's opinions could be said to be 'truer' than another's. For each is the sole judge of his or her own experiences." - Protagoras
'**********************************************
'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.
Ariston Collander
"No one person's opinions could be said to be 'truer' than another's. For each is the sole judge of his or her own experiences." - Protagoras
[quote user="dunnry"]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();
ThePharaoh
Member
135 Points
27 Posts
Getting object name instead of value from LDAP query
Apr 20, 2005 07:30 PM|LINK
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.
"No one person's opinions could be said to be 'truer' than another's. For each is the sole judge of his or her own experiences." - Protagoras
ThePharaoh
Member
135 Points
27 Posts
Re: Getting object name instead of value from LDAP query
Apr 21, 2005 08:06 PM|LINK
"No one person's opinions could be said to be 'truer' than another's. For each is the sole judge of his or her own experiences." - Protagoras
dunnry
Star
9098 Points
1806 Posts
Re: Getting object name instead of value from LDAP query
Apr 21, 2005 10:45 PM|LINK
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.
Weblog
The Book
LDAP Programming Help
ThePharaoh
Member
135 Points
27 Posts
Re: Getting object name instead of value from LDAP query
Apr 21, 2005 10:50 PM|LINK
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.
"No one person's opinions could be said to be 'truer' than another's. For each is the sole judge of his or her own experiences." - Protagoras
dunnry
Star
9098 Points
1806 Posts
Re: Getting object name instead of value from LDAP query
Apr 21, 2005 11:16 PM|LINK
//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.
Weblog
The Book
LDAP Programming Help
ThePharaoh
Member
135 Points
27 Posts
Re: Getting object name instead of value from LDAP query
Apr 27, 2005 03:08 PM|LINK
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!
"No one person's opinions could be said to be 'truer' than another's. For each is the sole judge of his or her own experiences." - Protagoras
ThePharaoh
Member
135 Points
27 Posts
Re: Getting object name instead of value from LDAP query
Apr 27, 2005 04:32 PM|LINK
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.
"No one person's opinions could be said to be 'truer' than another's. For each is the sole judge of his or her own experiences." - Protagoras
mychucky
Contributor
4358 Points
3722 Posts
Re: Getting object name instead of value from LDAP query
Oct 11, 2006 07:45 PM|LINK
[quote user="dunnry"]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:
If I do, how do I find those out?
mychucky
Contributor
4358 Points
3722 Posts
Re: Getting object name instead of value from LDAP query
Oct 13, 2006 12:42 PM|LINK
aspnet_spnt
Member
95 Points
19 Posts
Re: Getting object name instead of value from LDAP query
Nov 17, 2006 06:01 PM|LINK
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??