Collection Class Usage

Table of contents:

We need to know just a few basic things about using the value collection classes in order to be productive. Let's examine this from a task-centric perspective. In each of these examples, assume we have a DirectoryEntry called entry and a SearchResult called result pointing to the same object in the directory.

Getting Single Values

This is probably the thing we will do most often. Typically, we will either use the Value property or access the first element in the collection:

object name = entry.Properties["name"].Value;
name = entry.Properties["name"][0];
name = result.Properties["name"][0];

Note that the first approach is slightly safer if we have not already checked for a null value, as the Value property will return null/Nothing. The other approaches will throw an exception if the attribute is null, as the array will not have a value at index 0.

Checking for Null Values

If we try to imagine an object in an LDAP directory as a row in a SQL database, the schema for the table would typically allow many null column values. The vast majority of attributes in most LDAP classes are optional and it is very common for objects to contain only a small fraction of the attributes allowed by the schema. This analogy is a little flimsy, and we explain why in the sidebar LDAP and Null Values, later in this chapter. However, it is useful for our purposes here.

As a result, we will be checking for null values frequently. This is just good defensive programming and will contribute greatly to the stability of our applications in production.

With PropertyValueCollection, we can do this by using the Value property:

if (entry.Properties["description"].Value != null)
{
 //do something interesting
}

An alternate approach is to check the Count property:

if (entry.Properties["description"].Count > 0)
{
 //do something interesting
}

Things change a bit with ResultPropertyValueCollection. Since it does not have a Value property, we cannot check it for a null value. This is also a situation where there are differences between versions 1.x and 2.0 of the framework. Namely, in version 1.x, a ResultPropertyValueCollection instance was not created if the attribute was not returned in the search, so checking a property like Count would generate a NullReferenceException. This behavior has changed in version 2.0, and a Result-PropertyValueCollection instance will be returned, making it safe to check the Count property without fear of an exception.

LDAP and Null Values

We are purposely being a little bit loose with the term "null" in our discussion. Unlike in the RDBMS world, where we have neat rows and columns that structure our data, the LDAP world does not correspond to this model exactly. If we consider an object to be a row, then the attributes are the columns in a table. If we check the intersection of any row and column, we expect either a value or null. However, this analogy breaks down a bit when we consider that null values do not really exist in LDAP. Either the object contains the attribute or it does not. If we compare it to the RDBMS world again, it would be as if each object was a row, and each row had different columns defined. It is not as if the object contained an attribute that we could find, and it contained a "null" value! The attribute simply does not exist, and from a developer's perspective, we infer this to mean it is null.

As such, when we say an attribute is null, we really mean that the attribute does not exist on the object. That our SDS objects return null for nonexistent attributes is only a result of our programming model.

Of course, it is less than ideal to have to remember how value collections will behave based on the version of the framework we are using, and it leads to fragile code. Instead, we should always check the ResultPropertyCollection or PropertyValueCollection first:

if (result.Properties.Contains("description"))
{
 //do something interesting
}

if (entry.Properties.Contains("description"))
{
 //do something interesting
}

It turns out that since the Contains method works for both Result-PropertyCollection and PropertyCollection classes, it tends to be easier to use this method consistently without needing to remember the details of either class or the differences due to versions of the framework. For this reason, we generally recommend using the Contains method, as it will work well in all circumstances and scenarios.

Checking for Multiple Values

Many attributes in LDAP allow multiple values and the basic design of the value collections assumes that any attribute may contain multiple values. In order to find out if an attribute actually contains multiple values, we can use the Count property:

if (entry.Properties["memberOf"].Count > 1)
{
 //do something interesting
}

if (result.Properties.Contains("memberOf"))
{
 if (result.Properties["memberOf"].Count > 1)
 {
 //do something interesting
 }
}

We would do something similar with a SearchResult/ResultPropertyValueCollection.

Another option is simply to enumerate the collection with foreach:

foreach (string groupDN in entry.Properties["memberOf"])
{
 //do something interesting
}

if (result.Properties.Contains("memberOf"))
{
 foreach (string groupDN in result.Properties["memberOf"])
 {
 //do something interesting
 }
}

 

Using the Value Property

One of the primary differences between PropertyValueCollection and ResultPropertyValueCollection is that the former contains a Value property. The Value property does one of three different things when reading an attribute value.

  • If the attribute does not exist on the object, it returns null/Nothing.
  • If the attribute contains a single value, it returns an Object that represents a scalar value of the type as marshaled by the .NET Framework (see Table 6.1).

    Table 6.1. LDAP Attribute Syntaxes with Their Matching Programmatic Data Types

    Syntax Name

    Object(DS-DN)

    LDAP Syntax

    2.5.5.1

    OM Syntax

    127

    ADSI Type

    ADSTYPE_DN_STRING

    COM Type

    VT_BSTR

    DirectoryEntry

    System.String

    DirectorySearcher

    System.String

    Notes

    Standard distinguished name (DN) syntax

    Syntax Name

    String(Object-Identifier)

    LDAP Syntax

    2.5.5.2

    OM Syntax

    6

    ADSI Type

    ADSTYPE_CASE_IGNORE_STRING

    COM Type

    VT_BSTR

    DirectoryEntry

    System.String

    DirectorySearcher

    System.String

    Notes

    Contains only digits and "."

    Syntax Name

    String(Teletex)

    LDAP Syntax

    2.5.5.4

    OM Syntax

    20

    ADSI Type

    ADSTYPE_CASE_IGNORE_STRING

    COM Type

    VT_BSTR

    DirectoryEntry

    System.String

    DirectorySearcher

    System.String

    Notes

    Case insensitive for searching; Teletex characters only

    Syntax Name

    String(Printable)

    LDAP Syntax

    2.5.5.5

    OM Syntax

    19

    ADSI Type

    ADSTYPE_PRINTABLE_STRING

    COM Type

    VT_BSTR

    DirectoryEntry

    System.String

    DirectorySearcher

    System.String

    Notes

    Case sensitive for searching; printable characters only

    Syntax Name

    String(IA5)

    LDAP Syntax

    2.5.5.5

    OM Syntax

    22

    ADSI Type

    ADSTYPE_PRINTABLE_STRING

    COM Type

    VT_BSTR

    DirectoryEntry

    System.String

    DirectorySearcher

    System.String

    Notes

    Case sensitive for searching; IA5 string

    Syntax Name

    String(Numeric)

    LDAP Syntax

    2.5.5.6

    OM Syntax

    18

    ADSI Type

    ADSTYPE_NUMERIC_STRING

    COM Type

    VT_BSTR

    DirectoryEntry

    System.String

    DirectorySearcher

    System.String

    Notes

    Contains only digits; rarely used in Active Directory

    Syntax Name

    Object(DN-Binary)

    LDAP Syntax

    2.5.5.7

    OM Syntax

    127

    ADSI Type

    ADSTYPE_DN_WITH_BINARY

    COM Type

    VT_DISPATCH (IADsDNWithBinary)

    DirectoryEntry

    System.__ComObject

    DirectorySearcher

    System.String

    Notes

    Also Object(OR-Name); used for associating a GUID with DN

    Syntax Name

    Boolean

    LDAP Syntax

    2.5.5.8

    OM Syntax

    1

    ADSI Type

    ADSTYPE_BOOLEAN

    COM Type

    VT_BOOL

    DirectoryEntry

    System.Boolean

    DirectorySearcher

    System.Boolean

    Notes

    Used for standard Boolean values

    Syntax Name

    Integer

    LDAP Syntax

    2.5.5.9

    OM Syntax

    2

    ADSI Type

    ADSTYPE_INTEGER

    COM Type

    VT_I4

    DirectoryEntry

    System.Int32

    DirectorySearcher

    System.Int32

    Notes

    Used for standard signed integers

    Syntax Name

    Enumeration

    LDAP Syntax

    2.5.5.9

    OM Syntax

    10

    ADSI Type

    ADSTYPE_INTEGER

    COM Type

    VT_I4

    DirectoryEntry

    System.Int32

    DirectorySearcher

    System.Int32

    Notes

    Used for enumerated values

    Syntax Name

    String(Octet)

    LDAP Syntax

    2.5.5.10

    OM Syntax

    4

    ADSI Type

    ADSTYPE_OCTET_STRING

    COM Type

    VT_UI1|VT_ARRAY

    DirectoryEntry

    System.Byte[]

    DirectorySearcher

    System.Byte[]

    Notes

    Used for arbitrary binary data

    Syntax Name

    Object(Replica-Link)

    LDAP Syntax

    2.5.5.10

    OM Syntax

    127

    ADSI Type

    ADSTYPE_OCTET_STRING

    COM Type

    VT_VARIANT

    DirectoryEntry

    System.Byte[]

    DirectorySearcher

    System.Byte[]

    Notes

    Used by the system only for replication

    Syntax Name

    String(UTC-Time)

    LDAP Syntax

    2.5.5.11

    OM Syntax

    23

    ADSI Type

    ADSTYPE_UTC_TIME

    COM Type

    VT_DATE

    DirectoryEntry

    System.DateTime

    DirectorySearcher

    System.DateTime

    Notes

    Used for date values; stored relative to UTC

    Syntax Name

    String(Generalized-Time)

    LDAP Syntax

    2.5.5.11

    OM Syntax

    24

    ADSI Type

    ADSTYPE_UTC_TIME

    COM Type

    VT_DATE

    DirectoryEntry

    System.DateTime

    DirectorySearcher

    System.DateTime

    Notes

    Used for date values; time zone information is included

    Syntax Name

    String(Unicode)

    LDAP Syntax

    2.5.5.12

    OM Syntax

    64

    ADSI Type

    ADSTYPE_CASE_IGNORE_STRING

    COM Type

    VT_BSTR

    DirectoryEntry

    System.String

    DirectorySearcher

    System.String

    Notes

    Case insensitive for searching; contains any Unicode character

    Syntax Name

    Object(Presentation-Address)

    LDAP Syntax

    2.5.5.13

    OM Syntax

    127

    ADSI Type

    ADSTYPE_CASE_IGNORE_STRING

    COM Type

    VT_BSTR

    DirectoryEntry

    System.String

    DirectorySearcher

    System.String

    Notes

    Not really used in Active Directory either

    Syntax Name

    Object(DN-String)

    LDAP Syntax

    2.5.5.14

    OM Syntax

    127

    ADSI Type

    ADSTYPE_DN_WITH_STRING

    COM Type

    VT_DISPATCH (IADsDNWithString)

    DirectoryEntry

    System.__ComObject

    DirectorySearcher

    System.String

    Notes

    Not used in Active Directory schema; also defined as Object
    (Access-Point) which is not used and has no marshaling defined

    Syntax Name

    String(NT-Sec-Desc)

    LDAP Syntax

    2.5.5.15

    OM Syntax

    66

    ADSI Type

    ADSTYPE_NT_SECURITY_DESCRIPTOR

    COM Type

    VT_DISPATCH (IADsSecurityDescriptor)

    DirectoryEntry

    System.__ComObject

    DirectorySearcher

    System.Byte[]

    Notes

    Contains Windows security descriptors

    Syntax Name

    Interval/LargeInteger

    LDAP Syntax

    2.5.5.16

    OM Syntax

    65

    ADSI Type

    ADSTYPE_LARGE_INTEGER

    COM Type

    VT_DISPATCH (IADsLargeInteger)

    DirectoryEntry

    System.__ComObject

    DirectorySearcher

    System.Int64

    Notes

    Both types have same syntaxes, but Interval is treated as unsigned

    Syntax Name

    String(Sid)

    LDAP Syntax

    2.5.5.17

    OM Syntax

    4

    ADSI Type

    ADSTYPE_OCTET_STRING

    COM Type

    VT_UI1|VT_ARRAY

    DirectoryEntry

    System.Byte[]

    DirectorySearcher

    System.Byte[]

    Notes

    Contains Windows security identifiers

  • If the attribute contains multiple values, it returns an Object that contains a single-dimensional array of similarly typed objects.

For example, let's take the member attribute on the group class. It is defined as syntax 2.5.5.1, which is represented in .NET as System.String (see Table 6.1). It is defined in the schema as multivalued and optional. As such, the Value property might return null, a System.String, or an array of System.String objects, depending on whether the group has zero, one, or multiple members.

This makes the Value property especially useful for getting attribute values directly, as we have seen in the earlier examples and throughout the book. It can also be used for writing attribute values, where it is even more useful.

Part I: Fundamentals

Introduction to LDAP and Active Directory

Introduction to .NET Directory Services Programming

Binding and CRUD Operations with DirectoryEntry

Searching with the DirectorySearcher

Advanced LDAP Searches

Reading and Writing LDAP Attributes

Active Directory and ADAM Schema

Security in Directory Services Programming

Introduction to the ActiveDirectory Namespace

Part II: Practical Applications

User Management

Group Management

Authentication

Part III: Appendixes

Appendix A. Three Approaches to COM Interop with ADSI

Appendix B. LDAP Tools for Programmers

Appendix C. Troubleshooting and Help

Index



The. NET Developer's Guide to Directory Services Programming
The .NET Developers Guide to Directory Services Programming
ISBN: 0321350170
EAN: 2147483647
Year: 2004
Pages: 165

Similar book on Amazon

Flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net