Basics of Writing Attribute Values
Much of the information we have learned about reading attribute data we can also apply to writing. However, there are a few basic rules to understand right off the bat.
- We must use the DirectoryEntry family of classes for writing attribute data. The DirectorySearcher family is strictly read-only.
- We must use the LDAP provider with Active Directory for updates. The global catalog is read-only. Therefore, any object obtained from the global catalog is also read-only, regardless of whether it is a DirectoryEntry object.
- If we are using the property cache (which we always recommend), we must call CommitChanges to write any changes back to the directory. All changes are made to the in-memory property cache and will not be written to the server unless this is done.
- The directory will allow us to modify only those attributes that we have permission to modify.
- The directory will allow us to perform only those modifications that the schema allows, and potentially other specific rules governing writes to the attribute that are enforced by the directory.
- The directory will allow us to add only those values of the type defined by the attribute's schema.
The rules may seem obvious by now, but they come up as issues surprisingly often in practice.
DirectorySearcher makes it easy to switch to "edit mode" by using the SearchResult.GetDirectoryEntry method:
//given a SearchResult result that you wish to modify using (DirectoryEntry entry = result.GetDirectoryEntry()) { //now, perform modifications on the DirectoryEntry }
To switch from the GC provider to LDAP, create a new DirectoryEntry object with the LDAP provider.
Note: Switching from GC to LDAP May Require Changing Physical Servers Too
In order to switch from GC to LDAP, we need a domain controller in the domain where the object "lives." The global catalog server we were querying might be from a different domain, so this is not always straightforward.
Setting Initial Values
If an attribute has no value, we can use one of the following two approaches to set an initial value.
- Use the PropertyValueCollection.Add method.
- Use the PropertyValueCollection.Value method.
For example, to set the description attribute on a DirectoryEntry called entry, we might do this:
entry.Properties["description"].Add("a new description"); //or entry.Properties["description"].Value = "a new description"; entry.CommitChanges();
We recommend avoiding using the array index accessor for setting values in general and especially for setting initial values, as it does not behave exactly the same way against all versions of the .NET Framework and ADSI, and it might cause unexpected problems with routine upgrades and service packs. For example:
//don't do this; it might not work as expected entry.Properties["description"][0] = "a new description";
If we wish to set multiple values initially, we can use the Value property again, use the AddRange method, or call Add repeatedly:
//Do this entry.Properties["otherTelephone"].Value = new string[] {"222-222-2222", "333-333-3333"}; //or this entry.Properties["otherTelephone"].AddRange(new string[] {"222-222-2222", "333-333-3333"}); //or this entry.Properties["otherTelephone"].Add("222-222-2222"); entry.Properties["otherTelephone"].Add("333-333-3333"}); entry.CommitChanges();
It may seem obvious, but keep in mind that using the Value property overwrites the entire attribute, which is why it is appropriate for setting initial values and not for simply adding to the existing values.
Clearing an Attribute
If an attribute is set and we wish to clear it, we can simply call the Clear method:
entry.Properties["description"].Clear(); entry.CommitChanges();
Alternately, the Value property can be used to clear an attribute value by setting it to null (Nothing in Visual Basic), but the Clear method seems to convey our intentions better.
Replacing an Existing Attribute Value
If the attribute value is already populated and we want to replace it completely with a different value, the easiest way to do this is with the Value property. Using our first example again:
entry.Properties["description"].Value = "a second description"; entry.CommitChanges();
When we set the Value property, it has the benefit of completely replacing the existing value with whatever we set in a single LDAP modification operation.
Adding and Removing Values from Multivalued Attributes
If the attribute has multiple values and we wish to modify only parts of it, we should use the Add, AddRange, and Remove methods. This is a very common thing to do when we're modifying membership on a group and we want to add or remove individual members:
//given a DirectoryEntry entry bound to a group: entry.Properties["member"].Add( "CN=someuser,CN=Users,DC=domain,DC=com"); entry.Properties["member"].Remove( "CN=someotheruser,CN=Users,DC=domain,DC=com"); entry.CommitChanges();
If we are careful to check the values in the attribute before modification using the Contains method, we can avoid errors caused by adding duplicate entries or removing nonexistent entries.
Attribute Modification Summary
As we have shown, the Value property is extremely useful for attribute modifications. We can use it in just about any situation to set single and multiple values. It is also very efficient, as it uses a single PutEx call under the hood that results in a single LDAP modification operation. Additionally, the Add, AddRange, Remove, and Clear methods are very helpful and efficient under the hood, for similar reasons.
We generally recommend avoiding using array indexers on PropertyValueCollection for modifications. At the very least, changing a value using the array index will result in a remove and an add operation under the hood. In some situations, this simply will not work at all. The upcoming sidebar, Caution with Attribute Writing, provides more details about this problem. The other point here is that LDAP does not guarantee that multivalued attributes are stored or read in any specific order, so it is best not to think about them in that way, either.