Adding member to group with more than 1,000 members

Adding member to group with more than 1,000 members

Post by Marc » Fri, 30 May 2003 00:10:12



Hi,

I am having some difficulty adding a member to a group
object that has 1,000 or more members already in it.  For
example, if I have a group object with 1,300 existing
members in it, and I attempt to add another member, my
list of members is truncated to 1,001.  Since I am using a
Windows 2000 directory server, I assume it's related to
the 1,000 member limitation.  What I don't understand is
why my members are truncated when I'm adding only 1
member...

Here is the code I am using to add the member:

*****************BEGIN CODE********************
string ADsPath
= "LDAP://CN=GroupObject,DC=DomainA,DC=CorpA,DC=COM";
DirectoryEntry de = new DirectoryEntry(ADsPath);

// property value collection
PropertyValueCollection coll = de.Properties["member"];
coll.Add("CN=user,DC=DomainA,DC=CorpA,DC=com");

// commit
de.CommitChanges();
******************END CODE********************

Thanks in advance for your help. ;)

 
 
 

Adding member to group with more than 1,000 members

Post by MVP - ADS » Fri, 30 May 2003 01:44:01


This is my biggest pet peeve with System.DirectoryServices.  The
PropertyValueCollection only returns the first 1000 values in an attribute.
This in and of itself is not necessarily terrible, but the other problem is
that instead of using the Add and Delete PutEx functions under the hood to
update an attribute, it always uses Replace and recopies the entire
collection in memory.  The net result is that if the attribute contained
more than 1000 values, it will be truncated to include only the values that
were retrieved in the initial pull of the first 1000 values plus whatever
you have added since then.  Note that this was true in Framework 1.0.  I
haven't verified this behavior in 1.1.

There are a couple of workarounds for this:

 - Invoke the underlying IADsGroup Add and Remove methods.  Note that you
must supply a full LDAP URL instead of just a DN to these methods.
 - Invoke the IADs.PutEx method directly with the Add or Remove flags.  This
should prevent the PropertyValueCollection from modifying the attribute.
 - Use attribute ranging to pull back the complete member list into a
different collection (ArrayList works well for this) and do your operations
on it.  Then update the entire PropertyValueCollection with the values in
the ArrayList.  Here is my current VB.NET (easily convertible to C#)
attribute retrieval function that switches to ranging if 1000 or more
elements are encountered.

        Protected Shared Function GetAllAttributeValues(ByVal entry As
DirectoryEntry, ByVal attributeName As String) As ArrayList
            Dim propValues As PropertyValueCollection
            Dim propValue As Object
            Dim attributeValues As PropertyValueCollection
            Dim values As ArrayList

            Dim currentRange As String

            Dim startCount As Integer
            Dim endCount As Integer
            Dim iteration As Integer

            Dim increment As Integer = 1000
            Dim expectedErrorCode As Integer = -2147016672

            'This optimization reads the attribute directly if it
            'contains less than 1000 values and returns an arraylist based
            'on that.  If we have 1000 values, we assume that there are
likely more than
            '1000 values and we resort to the slower attribute ranging
method
            'done below
            entry.RefreshCache(New String() {attributeName})

            attributeValues = entry.Properties(attributeName)
            If attributeValues.Count < 1000 Then
                Dim memberValue As Object
                values = New ArrayList(attributeValues.Count)
                For Each memberValue In attributeValues
                    values.Add(memberValue)
                Next
                values.TrimToSize()
                Return values
            End If

            'here we go into ranging mode

            values = New ArrayList(1000)

            Do
                startCount = iteration * increment
                endCount = (iteration + 1) * increment - 1
                'This is the attribute ranging method for retrieving the
contents of large attributes
                currentRange = String.Format("{0};Range={1}-{2}",
attributeName, startCount, endCount)
                'this will throw when the lower bound on the range is too
high
                Try
                    entry.RefreshCache(New String() {currentRange})
                Catch e As COMException 'I might check for the expected
hresult (see above), but I don't know if I need to
                    Exit Do
                End Try

                'Get the values for the current range of attributes
                propValues = entry.Properties(attributeName)

                For Each propValue In propValues
                    values.Add(propValue)
                Next

                iteration += 1
                values.Capacity += increment

            Loop

            values.TrimToSize()
            Return values
        End Function

I hope this helps you.  Let us know...

Joe K.


Quote:> Hi,

> I am having some difficulty adding a member to a group
> object that has 1,000 or more members already in it.  For
> example, if I have a group object with 1,300 existing
> members in it, and I attempt to add another member, my
> list of members is truncated to 1,001.  Since I am using a
> Windows 2000 directory server, I assume it's related to
> the 1,000 member limitation.  What I don't understand is
> why my members are truncated when I'm adding only 1
> member...

> Here is the code I am using to add the member:

> *****************BEGIN CODE********************
> string ADsPath
> = "LDAP://CN=GroupObject,DC=DomainA,DC=CorpA,DC=COM";
> DirectoryEntry de = new DirectoryEntry(ADsPath);

> // property value collection
> PropertyValueCollection coll = de.Properties["member"];
> coll.Add("CN=user,DC=DomainA,DC=CorpA,DC=com");

> // commit
> de.CommitChanges();
> ******************END CODE********************

> Thanks in advance for your help. ;)


 
 
 

Adding member to group with more than 1,000 members

Post by Marc » Fri, 30 May 2003 04:31:50


I am using your code (works great) to retrieve the 1,000+
DNs in one of our group objects.  Here's how I'm trying to
write a new value to it:

1) Check to see if there are > 1000 members.
2) If so, put list of existing members into an ArrayList
3) Add members I want to add to the ArrayList
4) Update entire PropertyValueCollection with values in
ArrayList

My code keeps throwing an exception stating that "The
object already exists." Must I first clear the members
from the group object?  Now here's part of the code that's
not working:

// ********** BEGIN ***********
// assume more than 1,000 members
string AdsPath = "....";
DirectoryEntry de = new DirectoryEntry(AdsPath);
PropertyValueCollection members = de.Properties["member"];

ArrayList alTemp = GetListMembers(AdsPath);
string obItems[] = new string[alTemp.Count];

alTemp.CopyTo(obItems); // copy ArrayList to array[]

// add obItems[] to PropertyValueCollection
members.AddRange(obItems);

// clear existing members??
de.Properties["member"].Clear();

// add new members
de.Properties["member"].AddRange(members);

de.CommitChanges(); // throws exception.
// ********** END ***********

>-----Original Message-----
>This is my biggest pet peeve with

System.DirectoryServices.  The
>PropertyValueCollection only returns the first 1000

values in an attribute.
>This in and of itself is not necessarily terrible, but

the other problem is
>that instead of using the Add and Delete PutEx functions
under the hood to
>update an attribute, it always uses Replace and recopies
the entire
>collection in memory.  The net result is that if the
attribute contained
>more than 1000 values, it will be truncated to include

only the values that
>were retrieved in the initial pull of the first 1000

values plus whatever
>you have added since then.  Note that this was true in
Framework 1.0.  I
>haven't verified this behavior in 1.1.

>There are a couple of workarounds for this:

> - Invoke the underlying IADsGroup Add and Remove

methods.  Note that you
>must supply a full LDAP URL instead of just a DN to these
methods.
> - Invoke the IADs.PutEx method directly with the Add or
Remove flags.  This
>should prevent the PropertyValueCollection from modifying
the attribute.
> - Use attribute ranging to pull back the complete member
list into a
>different collection (ArrayList works well for this) and
do your operations
>on it.  Then update the entire PropertyValueCollection
with the values in
>the ArrayList.  Here is my current VB.NET (easily
convertible to C#)
>attribute retrieval function that switches to ranging if
1000 or more
>elements are encountered.

>        Protected Shared Function GetAllAttributeValues
(ByVal entry As
>DirectoryEntry, ByVal attributeName As String) As
ArrayList
>            Dim propValues As PropertyValueCollection
>            Dim propValue As Object
>            Dim attributeValues As PropertyValueCollection
>            Dim values As ArrayList

>            Dim currentRange As String

>            Dim startCount As Integer
>            Dim endCount As Integer
>            Dim iteration As Integer

>            Dim increment As Integer = 1000
>            Dim expectedErrorCode As Integer = -2147016672

>            'This optimization reads the attribute
directly if it
>            'contains less than 1000 values and returns
an arraylist based
>            'on that.  If we have 1000 values, we assume
that there are
>likely more than
>            '1000 values and we resort to the slower
attribute ranging
>method
>            'done below
>            entry.RefreshCache(New String()
{attributeName})

>            attributeValues = entry.Properties
(attributeName)
>            If attributeValues.Count < 1000 Then
>                Dim memberValue As Object
>                values = New ArrayList

(attributeValues.Count)

- Show quoted text -

>                For Each memberValue In attributeValues
>                    values.Add(memberValue)
>                Next
>                values.TrimToSize()
>                Return values
>            End If

>            'here we go into ranging mode

>            values = New ArrayList(1000)

>            Do
>                startCount = iteration * increment
>                endCount = (iteration + 1) * increment - 1
>                'This is the attribute ranging method for
retrieving the
>contents of large attributes
>                currentRange = String.Format("{0};Range=
{1}-{2}",
>attributeName, startCount, endCount)
>                'this will throw when the lower bound on
the range is too
>high
>                Try
>                    entry.RefreshCache(New String()
{currentRange})
>                Catch e As COMException 'I might check
for the expected
>hresult (see above), but I don't know if I need to
>                    Exit Do
>                End Try

>                'Get the values for the current range of
attributes
>                propValues = entry.Properties
(attributeName)

>                For Each propValue In propValues
>                    values.Add(propValue)
>                Next

>                iteration += 1
>                values.Capacity += increment

>            Loop

>            values.TrimToSize()
>            Return values
>        End Function

>I hope this helps you.  Let us know...

>Joe K.

>"Marc" <mmerr...@dc.com.NOSPAMMERS> wrote in message
>news:3e3001c3252b$3c4a0050$a001280a@phx.gbl...
>> Hi,

>> I am having some difficulty adding a member to a group
>> object that has 1,000 or more members already in it.  
For
>> example, if I have a group object with 1,300 existing
>> members in it, and I attempt to add another member, my
>> list of members is truncated to 1,001.  Since I am
using a
>> Windows 2000 directory server, I assume it's related to
>> the 1,000 member limitation.  What I don't understand is
>> why my members are truncated when I'm adding only 1
>> member...

>> Here is the code I am using to add the member:

>> *****************BEGIN CODE********************
>> string ADsPath
>> = "LDAP://CN=GroupObject,DC=DomainA,DC=CorpA,DC=COM";
>> DirectoryEntry de = new DirectoryEntry(ADsPath);

>> // property value collection
>> PropertyValueCollection coll = de.Properties["member"];
>> coll.Add("CN=user,DC=DomainA,DC=CorpA,DC=com");

>> // commit
>> de.CommitChanges();
>> ******************END CODE********************

>> Thanks in advance for your help. ;)

>.

 
 
 

Adding member to group with more than 1,000 members

Post by MVP - ADS » Fri, 30 May 2003 05:28:51


I'm not sure what the error is, but I think you can get rid of the first
AddRange and just do the second one.  You may also need to call Clear()
twice to get it to remove the members.  I had that problem once and I never
figured out why, but it was definitely necessary in some instances.  Perhaps
a bug in the Win2K DS client that doesn't show up in XP?  There is at least
one other one of those.

Joe K.

"Marc" <mmerr...@dc.com.NOSPAMMERS> wrote in message

news:452b01c3254f$c94c97f0$a601280a@phx.gbl...
> I am using your code (works great) to retrieve the 1,000+
> DNs in one of our group objects.  Here's how I'm trying to
> write a new value to it:

> 1) Check to see if there are > 1000 members.
> 2) If so, put list of existing members into an ArrayList
> 3) Add members I want to add to the ArrayList
> 4) Update entire PropertyValueCollection with values in
> ArrayList

> My code keeps throwing an exception stating that "The
> object already exists." Must I first clear the members
> from the group object?  Now here's part of the code that's
> not working:

> // ********** BEGIN ***********
> // assume more than 1,000 members
> string AdsPath = "....";
> DirectoryEntry de = new DirectoryEntry(AdsPath);
> PropertyValueCollection members = de.Properties["member"];

> ArrayList alTemp = GetListMembers(AdsPath);
> string obItems[] = new string[alTemp.Count];

> alTemp.CopyTo(obItems); // copy ArrayList to array[]

> // add obItems[] to PropertyValueCollection
> members.AddRange(obItems);

> // clear existing members??
> de.Properties["member"].Clear();

> // add new members
> de.Properties["member"].AddRange(members);

> de.CommitChanges(); // throws exception.
> // ********** END ***********

> >-----Original Message-----
> >This is my biggest pet peeve with
> System.DirectoryServices.  The
> >PropertyValueCollection only returns the first 1000
> values in an attribute.
> >This in and of itself is not necessarily terrible, but
> the other problem is
> >that instead of using the Add and Delete PutEx functions
> under the hood to
> >update an attribute, it always uses Replace and recopies
> the entire
> >collection in memory.  The net result is that if the
> attribute contained
> >more than 1000 values, it will be truncated to include
> only the values that
> >were retrieved in the initial pull of the first 1000
> values plus whatever
> >you have added since then.  Note that this was true in
> Framework 1.0.  I
> >haven't verified this behavior in 1.1.

> >There are a couple of workarounds for this:

> > - Invoke the underlying IADsGroup Add and Remove
> methods.  Note that you
> >must supply a full LDAP URL instead of just a DN to these
> methods.
> > - Invoke the IADs.PutEx method directly with the Add or
> Remove flags.  This
> >should prevent the PropertyValueCollection from modifying
> the attribute.
> > - Use attribute ranging to pull back the complete member
> list into a
> >different collection (ArrayList works well for this) and
> do your operations
> >on it.  Then update the entire PropertyValueCollection
> with the values in
> >the ArrayList.  Here is my current VB.NET (easily
> convertible to C#)
> >attribute retrieval function that switches to ranging if
> 1000 or more
> >elements are encountered.

> >        Protected Shared Function GetAllAttributeValues
> (ByVal entry As
> >DirectoryEntry, ByVal attributeName As String) As
> ArrayList
> >            Dim propValues As PropertyValueCollection
> >            Dim propValue As Object
> >            Dim attributeValues As PropertyValueCollection
> >            Dim values As ArrayList

> >            Dim currentRange As String

> >            Dim startCount As Integer
> >            Dim endCount As Integer
> >            Dim iteration As Integer

> >            Dim increment As Integer = 1000
> >            Dim expectedErrorCode As Integer = -2147016672

> >            'This optimization reads the attribute
> directly if it
> >            'contains less than 1000 values and returns
> an arraylist based
> >            'on that.  If we have 1000 values, we assume
> that there are
> >likely more than
> >            '1000 values and we resort to the slower
> attribute ranging
> >method
> >            'done below
> >            entry.RefreshCache(New String()
> {attributeName})

> >            attributeValues = entry.Properties
> (attributeName)
> >            If attributeValues.Count < 1000 Then
> >                Dim memberValue As Object
> >                values = New ArrayList
> (attributeValues.Count)
> >                For Each memberValue In attributeValues
> >                    values.Add(memberValue)
> >                Next
> >                values.TrimToSize()
> >                Return values
> >            End If

> >            'here we go into ranging mode

> >            values = New ArrayList(1000)

> >            Do
> >                startCount = iteration * increment
> >                endCount = (iteration + 1) * increment - 1
> >                'This is the attribute ranging method for
> retrieving the
> >contents of large attributes
> >                currentRange = String.Format("{0};Range=
> {1}-{2}",
> >attributeName, startCount, endCount)
> >                'this will throw when the lower bound on
> the range is too
> >high
> >                Try
> >                    entry.RefreshCache(New String()
> {currentRange})
> >                Catch e As COMException 'I might check
> for the expected
> >hresult (see above), but I don't know if I need to
> >                    Exit Do
> >                End Try

> >                'Get the values for the current range of
> attributes
> >                propValues = entry.Properties
> (attributeName)

> >                For Each propValue In propValues
> >                    values.Add(propValue)
> >                Next

> >                iteration += 1
> >                values.Capacity += increment

> >            Loop

> >            values.TrimToSize()
> >            Return values
> >        End Function

> >I hope this helps you.  Let us know...

> >Joe K.

> >"Marc" <mmerr...@dc.com.NOSPAMMERS> wrote in message
> >news:3e3001c3252b$3c4a0050$a001280a@phx.gbl...
> >> Hi,

> >> I am having some difficulty adding a member to a group
> >> object that has 1,000 or more members already in it.
> For
> >> example, if I have a group object with 1,300 existing
> >> members in it, and I attempt to add another member, my
> >> list of members is truncated to 1,001.  Since I am
> using a
> >> Windows 2000 directory server, I assume it's related to
> >> the 1,000 member limitation.  What I don't understand is
> >> why my members are truncated when I'm adding only 1
> >> member...

> >> Here is the code I am using to add the member:

> >> *****************BEGIN CODE********************
> >> string ADsPath
> >> = "LDAP://CN=GroupObject,DC=DomainA,DC=CorpA,DC=COM";
> >> DirectoryEntry de = new DirectoryEntry(ADsPath);

> >> // property value collection
> >> PropertyValueCollection coll = de.Properties["member"];
> >> coll.Add("CN=user,DC=DomainA,DC=CorpA,DC=com");

> >> // commit
> >> de.CommitChanges();
> >> ******************END CODE********************

> >> Thanks in advance for your help. ;)

> >.

 
 
 

1. List members of Local Group on Member Server

I am looking for a code snippet to show me how to use ADSI
or WMI to get to the information I need.  My specific
problem is when getting the domain name of the object.  An
example would be the local Administrators group has two
global groups in it named x/Domain Admins and y/Domain
Admins.  When I list the members to a text file I lose the
domain the global groups is from.

2. Exchange 5.5 Email Client

3. Member of 1000 groups limit

4. example of TSR in C

5. Global groups within group groups - how do i get member list

6. Linking against an old version of SMGSHR. How to???

7. domain users group added, but members are denied access

8. Selecting the a node

9. Adding members to Groups in SQL Server

10. Can you add Members from an External, Trusted Domain to a Group using C#?

11. Enumeration of global groups member of another group...

12. Getting the list of groups in which a user/group is a member.

13. Getting the groups a user is a member of doesn't work for hierarchical groups