Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chriss Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How to populate a dictionary object from a recordset?

Status
Not open for further replies.

Exidy

Technical User
Feb 26, 2005
12
US
Hopefully you guys can help with this.

Here's a chunk of code that looks at every user in an OU and plucks out those users' homeDirectory and userPrincipalName attributes:

Code:
strBase = "<LDAP://ou=Users,ou=Site1,ou=Sites,dc=mycompany,dc=com>"
strFilter = "(&(objectCategory=person)(objectClass=user))"
strAttributes = "homeDirectory,userPrincipalName"
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
objCommand.CommandText = strQuery

Set objRecordSet = objCommand.Execute

objRecordSet.MoveFirst 
Do Until objRecordSet.EOF 
  strHomeDirectory = objRecordSet.Fields("homeDirectory").Value
  strUsername = objRecordSet.Fields("userPrincipalName").Value
  For Each strHomeDirectory In objRecordSet.Fields
      If InStr(strHomeDirectory, "server01") > 0 then   
          Group1.Add strUsername, "server01"
      Else 
          If InStr(strHomeDirectory, "server02") > 0 Then 
              Group2.Add strUsername, "server02"
          End If  
       End If 
  Next  
  objRecordSet.MoveNext 
Loop

When I try to run it, though, the error returned is "This key is already associated with an element of this collection."

Basically I need a way to take all the homeDirectory and userPrincipalName attributes that the loop collects for all the users and stick them into two separate dictionary objects. So, at the end, I'd have two dictionary objects, one filled with a list of all users on server01, and one filled with a list of all users on server02.

What would be the best way to go about doing this?
 
Perhaps before adding the next item to the collection, check to see if there is an existing collection member associated with the key.
 
The thing is, though, the keys are AD userPrincipalNames and there won't be duplicates of those in Active Directory. The end result that I'm looking for is two dictionary objects - one with a keys corresponding to all the users on server01, and one with keys corresponding to all the users on server02. Like this:

Dictionary Object 1 (Group1)

Key Item
johnsmith server01
karenprice server01
bobhope server01

Dictionary Object 2 (Group2)

Key Item
timenchanter server02
thegoodwitch server02
misterT server02
 
The variable name "strHomeDirectory " in the following line is misleading:
[tt]For Each strHomeDirectory In objRecordSet.Fields[/tt]

A better name would be "objField":
[tt]For Each objField In objRecordSet.Fields[/tt]



Anyway, as long as you have more than one field in your recordset.. and you have 2... the code as written will attempt to insert a duplicate key on its second iteration... when it is processing the second field.
 
Why do you need the For/Next loop at all? Why not just rip it out?
 
Well, objRecordSet is supposed to be the collection of all users in a specified OU. I use objRecordSet.MoveNext to step through each user, and I grab the homeDirectory and userPrincipalName attributes from each. The For/Next loop is supposed to examine those attributes for each user, and add entries to either dictionary object accordingly. Am I approaching it the wrong way?
 
If you are getting an object exists error then the name you are using appears twice in AD. To get around this test for existence before you add:

For Each strHomeDirectory In objRecordSet.Fields
If InStr(strHomeDirectory, "server01") > 0 then
[red]If Not Group1.Exists(strUserName) Then[/red] Group1.Add strUsername, "server01"
Else
If InStr(strHomeDirectory, "server02") > 0 Then
[red]If Not Group2.Exists(strUserName) Then[/red] Group2.Add strUsername, "server02"
End If
End If
Next
objRecordSet.MoveNext
Loop

[red]"... isn't sanity really just a one trick pony anyway?! I mean, all you get is one trick, rational thinking, but when you are good and crazy, oooh, oooh, oooh, the sky is the limit!" - The Tick[/red]
 
And what about this ?
objRecordSet.MoveFirst
Do Until objRecordSet.EOF
strHomeDirectory = objRecordSet.Fields("homeDirectory").Value
strUsername = objRecordSet.Fields("userPrincipalName").Value
If InStr(strHomeDirectory, "server01") > 0 then
Group1.Add strUsername, "server01"
ElseIf InStr(strHomeDirectory, "server02") > 0 Then
Group2.Add strUsername, "server02"
End If
objRecordSet.MoveNext
Loop

Hope This Helps, PH.
Want to get great answers to your Tek-Tips questions? Have a look at FAQ219-2884 or FAQ181-2886
 
Yes you are thinking too hard.

The .MoveNext will switch between "rows" in your recordset.

Each row represents one person user in the OU.

Each "row" has two fields: homeDirectory and userPrincipleName.

so your code:[tt]
strHomeDirectory = objRecordSet.Fields("homeDirectory").Value
strUsername = objRecordSet.Fields("userPrincipalName").Value[/tt]
... that code gets these values for the current "row"... AKA the current user.

So then when you do .MoveNext you go to the next user.



 
Let me see if I'm reading you guys right...

If .MoveNext is already doing the work of moving me to the next user, then I have to remove the For/Next loop.

And if it tells me there's a duplicate, it's because there's another user with the same userPrincipalName in AD.

I thought it was impossible for two AD users to share the same userPrincipalName? Or is that another attribute?
 
Correct, as originally written the For/Next loop was moving between the two attributes of the person. (ie: the two fields of the row)

 
Why not just use two clean queries and two Recordsets, then disconnect them both.

A disconnected Recordset makes a fine multi-valued collection or dictionary object by itself. Plus you can filter or sort on more than just one "key" field, persist it if desired, and do many other things with it.

What do you gain by copying the stuff into Dictionary objects anyway?
 
Well, I was just looking at the dictionary object as a convenient way to store two-part data (username, server name). I've never used a disonencted recordset before, but I'm always willing to learn of course.

The script is running now and returning real numbers - except that the number of its users it's returning for server02 is *much* lower than what it should be. I don't think my initial AD query (objCommand) is the problem since when I use that to gain a number of total users, the number for that is pretty accurate.
 
Try to compare ignoring case:
If InStr([!]1, [/!]strHomeDirectory, "server01"[!], 1[/!]) > 0 then
Group1.Add strUsername, "server01"
ElseIf InStr([!]1, [/!]strHomeDirectory, "server02"[!], 1[/!]) > 0 Then
Group2.Add strUsername, "server02"
End If

Hope This Helps, PH.
Want to get great answers to your Tek-Tips questions? Have a look at FAQ219-2884 or FAQ181-2886
 
the number of its users it's returning for server02 is *much* lower than what it should be

Is it possible for one user to be one both servers? Because thats not the way the nested IF statements are configured.
 
PHV, I tried your suggestion but the results returned were the same.

Sheco, as far as I know the homeDirectory attribute can only have one server path, since homeDirectory corresponds to the Connect To field in the Profile tab of a user in the Active Directory Users & Computers console. That field can only hold one path.

There's been a new development, though: I took a direct look at server01 and server02 today and it looks like some users do have folders on both servers. I don't think they're straight duplicates, though; I think some of them may be leftovers from when most of the users were migrated to another server. That's just a guess.

If this is the case, then it looks like I won't be able to solve my problem just by examining the homeDirectory attribute of users. I'll have to use FileSystemObject to do a count of the folders and folder sizes on each server - which will require local access. Guess I'll have to have a talk with the senior admins, eh?
 
BTW, putting aside the problem of duplicate folders for the moment - I've also run across a few offsite servers that hold some of our users. I'd like to actively exclude those servers from both consideration in the loop and in the total user count.

What would be the best way to go about this?
 
You may be able to keep them out of your recordset by changing the syntax of the LDAP query.

If that doesn't work then you could add someting into your loop to exclude them during processing.

Both of these approaches rely on the ability to distinguish between the unwanted users and the remainder of the population.
 

If I'm reading that article correctly, there's no way to exclude the server names in the LDAP query itself. I'm guessing I'll have to write the exclusions into the actual loop.

I think this also means that I'll have to use the counts of the entries in my dictionary objects to get the total number of users, instead of getting that number from the LDAP query.
 
the link talks of LDAP search syntax...you can use standard SQL queries as well, if you can exclude stuff with an SQL query then you can apply it your search....
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top