Using Generics with System.DirectoryServices

M

Mark Rae

Hi,

I'm in the process of updating an ASP.NET v1.1 web app to v2. The app uses
ActiveDirectory a great deal, and I'm trying to use the new
System.Collections.Generic namespace where possible, having been advised by
several luminaries that that is a "good thing to do"... :)

However, I'm experiencing a problem with the IEnumerable interface. (N.B. I
understand fully that I should be using the LDAP provider instead of the
WinNT provider - I'm in the process of fixing that too, but that's not my
most pressing problem.)

I use the following function to return a list of all users in a group:

using System;
using System.Collections;
using System.Collections.Generic;
using System.DirectoryServices;

public static List<string> GetGroupsForUser(string pstrUser)
{
DirectoryEntry objADEntry = null;
DirectoryEntry objUser = null;
object objChildren = null;
List<string> lstGroups = new List<string>();

try
{
objADEntry = new DirectoryEntry("WinNT://" + mstrDomain + ",
domain");
objUser = objADEntry.Children.Find(pstrUser, "user");
objChildren = objUser.Invoke("Groups");
foreach (object objChild in (IEnumerable)objChildren)
{
DirectoryEntry objGroup = new DirectoryEntry(objChild);
lstGroups.Add(objGroup.Name.ToLower());
}
return lstGroups;
}
catch (Exception)
{
throw;
}
}


It works well enough. However, when I rem out the using System.Collections;
namespace reference, the code fails and tells me that there is no support
for IEnumerable taking no arguments. Fair enough.

If I amend the line in question to:

foreach (object objChild in (IEnumerable<DirectoryEntries>)objChildren)

That returns the following error:

Unable to cast COM object of type 'System.__ComObject' to interface type
'System.Collections.Generic.IEnumerable`1[System.DirectoryServices.DirectoryEntries]'.
This operation failed because the QueryInterface call on the COM component
for the interface with IID '{AEF9EC8A-1E73-365B-8DA2-800A3A6166E6}' failed
due to the following
error: No such interface supported (Exception from HRESULT: 0x80004002
(E_NOINTERFACE)).


At this point, I'm pretty much out of my depth. Therefore, I'd be grateful
to know:

1) if what I'm doing is a worthwhile exercise, or whether I should just
continue with the old IEnumerable object in the old System.Collections
namespace

2) apart from replacing WinNT with LDAP, which I'm in the process of doing,
is there a more efficient method for returning a list of groups to which a
user belongs?


Any assistance gratefully received.

Mark
 
S

Sean Chambers

All of my apps that use AD are still on .NET 1.1, but to the best of my
knowledge there have been a number of changes to DirectoryServices in
..net 2.0

First, I never used the WinNT objects, I always used LDAP. I didn't
need backwards compatibility and it just worked the first time I tried
it, so in that sense I'm not sure how the WinNT query prefix works

For more information on DirectoryServices take a look here:

http://msdn.microsoft.com/library/d...-us/adsi/adsi/implementing_adsi_providers.asp

that link talks about the ADSI providers which probably supports
generics. The method your trying doesn't seem to support generics,
especially since it seems to be returning a COM object.

hope that helps!

sean

Mark said:
Hi,

I'm in the process of updating an ASP.NET v1.1 web app to v2. The app uses
ActiveDirectory a great deal, and I'm trying to use the new
System.Collections.Generic namespace where possible, having been advised by
several luminaries that that is a "good thing to do"... :)

However, I'm experiencing a problem with the IEnumerable interface. (N.B. I
understand fully that I should be using the LDAP provider instead of the
WinNT provider - I'm in the process of fixing that too, but that's not my
most pressing problem.)

I use the following function to return a list of all users in a group:

using System;
using System.Collections;
using System.Collections.Generic;
using System.DirectoryServices;

public static List<string> GetGroupsForUser(string pstrUser)
{
DirectoryEntry objADEntry = null;
DirectoryEntry objUser = null;
object objChildren = null;
List<string> lstGroups = new List<string>();

try
{
objADEntry = new DirectoryEntry("WinNT://" + mstrDomain + ",
domain");
objUser = objADEntry.Children.Find(pstrUser, "user");
objChildren = objUser.Invoke("Groups");
foreach (object objChild in (IEnumerable)objChildren)
{
DirectoryEntry objGroup = new DirectoryEntry(objChild);
lstGroups.Add(objGroup.Name.ToLower());
}
return lstGroups;
}
catch (Exception)
{
throw;
}
}


It works well enough. However, when I rem out the using System.Collections;
namespace reference, the code fails and tells me that there is no support
for IEnumerable taking no arguments. Fair enough.

If I amend the line in question to:

foreach (object objChild in (IEnumerable<DirectoryEntries>)objChildren)

That returns the following error:

Unable to cast COM object of type 'System.__ComObject' to interface type
'System.Collections.Generic.IEnumerable`1[System.DirectoryServices.DirectoryEntries]'.
This operation failed because the QueryInterface call on the COM component
for the interface with IID '{AEF9EC8A-1E73-365B-8DA2-800A3A6166E6}' failed
due to the following
error: No such interface supported (Exception from HRESULT: 0x80004002
(E_NOINTERFACE)).


At this point, I'm pretty much out of my depth. Therefore, I'd be grateful
to know:

1) if what I'm doing is a worthwhile exercise, or whether I should just
continue with the old IEnumerable object in the old System.Collections
namespace

2) apart from replacing WinNT with LDAP, which I'm in the process of doing,
is there a more efficient method for returning a list of groups to which a
user belongs?


Any assistance gratefully received.

Mark
 
M

Mark Rae

The method your trying doesn't seem to support generics,
especially since it seems to be returning a COM object.

Er said:
hope that helps!

I'm afraid it doesn't really... But it's the only reply I've had, so thanks
for that... :)
 
S

Sean Chambers

The method your trying doesn't seem to support generics,
Er, my method is returning a List<string> object...

I was referring to the foreach where you are trying to cast a COM
object to a string.

I don't have any experience with com, perhaps I am confused?
 
W

Willy Denoyette [MVP]

| Hi,
|
| I'm in the process of updating an ASP.NET v1.1 web app to v2. The app uses
| ActiveDirectory a great deal, and I'm trying to use the new
| System.Collections.Generic namespace where possible, having been advised
by
| several luminaries that that is a "good thing to do"... :)
|
| However, I'm experiencing a problem with the IEnumerable interface. (N.B.
I
| understand fully that I should be using the LDAP provider instead of the
| WinNT provider - I'm in the process of fixing that too, but that's not my
| most pressing problem.)
|
| I use the following function to return a list of all users in a group:
|
| using System;
| using System.Collections;
| using System.Collections.Generic;
| using System.DirectoryServices;
|
| public static List<string> GetGroupsForUser(string pstrUser)
| {
| DirectoryEntry objADEntry = null;
| DirectoryEntry objUser = null;
| object objChildren = null;
| List<string> lstGroups = new List<string>();
|
| try
| {
| objADEntry = new DirectoryEntry("WinNT://" + mstrDomain + ",
| domain");
| objUser = objADEntry.Children.Find(pstrUser, "user");
| objChildren = objUser.Invoke("Groups");
| foreach (object objChild in (IEnumerable)objChildren)
| {
| DirectoryEntry objGroup = new DirectoryEntry(objChild);
| lstGroups.Add(objGroup.Name.ToLower());
| }
| return lstGroups;
| }
| catch (Exception)
| {
| throw;
| }
| }
|
|
| It works well enough. However, when I rem out the using
System.Collections;
| namespace reference, the code fails and tells me that there is no support
| for IEnumerable taking no arguments. Fair enough.
|
| If I amend the line in question to:
|
| foreach (object objChild in (IEnumerable<DirectoryEntries>)objChildren)
|
| That returns the following error:
|
| Unable to cast COM object of type 'System.__ComObject' to interface type
|
'System.Collections.Generic.IEnumerable`1[System.DirectoryServices.DirectoryEntries]'.
| This operation failed because the QueryInterface call on the COM component
| for the interface with IID '{AEF9EC8A-1E73-365B-8DA2-800A3A6166E6}' failed
| due to the following
| error: No such interface supported (Exception from HRESULT: 0x80004002
| (E_NOINTERFACE)).
|
|
| At this point, I'm pretty much out of my depth. Therefore, I'd be grateful
| to know:
|
| 1) if what I'm doing is a worthwhile exercise, or whether I should just
| continue with the old IEnumerable object in the old System.Collections
| namespace
|
| 2) apart from replacing WinNT with LDAP, which I'm in the process of
doing,
| is there a more efficient method for returning a list of groups to which a
| user belongs?
|
|
| Any assistance gratefully received.
|
| Mark
|
|

WinNT and LDAP (the providers) implement a diferent object model, so you
can't use the following sample to query group membership in a NT4 domain.

// search all groups myUser is member a of.
// use server or FastBind binding for the fastest way to query the AD.
using(DirectoryEntry user = new DirectoryEntry("LDAP://..., cn=myUser,
cn=.., dn=..", AuthenticationTypes.FastBind))
{
PropertyCollection pcoll = user.Properties;
PropertyValueCollection memberOf = pcoll["memberOf"];
foreach(string group in memberOf)
lstGroups.Add(group);
}

note also that the AD hierarchy differs considerable from NT4's SAM object
model, so the data returned is in the "common name "format (that is :
CN=...) instead of a simple SAM account name.

Willy.
 
M

Mark Rae

WinNT and LDAP (the providers) implement a diferent object model, so you
can't use the following sample to query group membership in a NT4 domain.

That's fine - it's a Win2k3 domain.
using(DirectoryEntry user = new DirectoryEntry("LDAP://..., cn=myUser,
cn=.., dn=..", AuthenticationTypes.FastBind))

That generates an error saying that there's no overload for DirectoryEntry
taking 2 arguments...

However, I'll perservere... :)
 
M

Mark Rae

However, I'll perservere... :)

Persevered and succeeded :) New code, with the added advantage of having
removed the reliance on the WinNT provider, follows:

public static List<string> GetGroupsForUser(string pstrUser)
{
DirectorySearcher objDS = null;
SearchResult objSR = null;
DirectoryEntry objUser = null;
List<string> lstGroups = new List<string>();

try
{
objDS = new DirectorySearcher("objectCategory=User");
objDS.Filter = "(SAMAccountName=" + pstrUser + ")";
objSR = objDS.FindOne();
objUser = new DirectoryEntry(objSR.Path);

PropertyCollection colProperties = objUser.Properties;
PropertyValueCollection colPropertyValues =
colProperties["memberOf"];
foreach (string strGroup in colPropertyValues)
{
lstGroups.Add(GetSAMAccountName(strGroup).ToLower());
}
return lstGroups;
}
catch (Exception)
{
throw;
}
}

GetSAMAccountName is, as its name suggests, another method in my ADSI
wrapper class which takes an LDAP path and returns the SAMAccountName -
thanks again for your help.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top