简体   繁体   中英

PrincipalSearchResult and System.OutOfMemoryException

I'm using Domain PrincipalContext to find users groups. And I got it. But when I'm trying to work with group collection I get System.OutOfMemoryException . All Principal objects are disposable. And there is using section in my code. I had tried to use Dispose() method and GC.Collect() But it does not help.

Here is code:

using (var ctx = new PrincipalContext(ContextType.Domain, _domain, _user, _password))
{
    using (UserPrincipal user = UserPrincipal.FindByIdentity(ctx,IdentityType.SamAccountName, sAMAccountName))
    {
        PrincipalSearchResult<Principal> userGroups = user.GetGroups();                    
        using (userGroups)
        {
            foreach (Principal p in userGroups)
            {
                using (p)
                {
                    result.Add(p.Guid == null ? Guid.Empty : (Guid)p.Guid);
                }
            }
        }
    }
}

foreach loop return exception. Even foreach is empty loop.

I find that the System.DirectoryServices.AccountManagement namespace ( UserPrincipal , etc.) does waste a lot of memory. For example, every time you create a UserPrincipal or GroupPrincipal , it asks AD for every attribute that has a value - even if you only ever use one of them.

If the user is a member of many, many groups, that could be the cause, although I am still surprised. Maybe your computer just doesn't have the available memory to load all that.

You can do the same thing and use less memory (and probably less time) by using the System.DirectoryServices namespace directly (which is what the AccountManagement namespace uses in the background anyway).

Here is an example that will look at the memberOf attribute of the user to find the groups and pull the Guid . This does have some limitations, which I describe an article I wrote: Finding all of a user's groups (this example is modified from one in that article). However, in most cases (especially if you only have one domain in your environment and no trusted domains) it'll be fine.

public static IEnumerable<Guid> GetUserMemberOf(DirectoryEntry de) {
    var groups = new List<Guid>();

    //retrieve only the memberOf attribute from the user
    de.RefreshCache(new[] {"memberOf"});

    while (true) {
        var memberOf = de.Properties["memberOf"];
        foreach (string group in memberOf) {
            using (var groupDe = new DirectoryEntry($"LDAP://{group.Replace("/", "\\/")}") {
                groupDe.RefreshCache(new[] {"objectGUID"});
                groups.Add(new Guid((byte[]) groupDe.Properties["objectGUID"].Value));
            }
        }

        //AD only gives us 1000 or 1500 at a time (depending on the server version)
        //so if we've hit that, go see if there are more
        if (memberOf.Count != 1500 && memberOf.Count != 1000) break;

        try {
            de.RefreshCache(new[] {$"memberOf;range={groups.Count}-*"});
        } catch (COMException e) {
            if (e.ErrorCode == unchecked((int) 0x80072020)) break; //no more results

            throw;
        }
    }
    return groups;
}

You need to feed it a DirectoryEntry object of the user. If you know the distinguishedName beforehand, you can use that (eg new DirectoryEntry($"LDAP://{distinguishedName}") ). But if not, you can search for it:

var ds = new DirectorySearcher(
                new DirectoryEntry($"LDAP://{_domain}"),
                $"(&(objectClass=user)(sAMAccountName={sAMAccountName}))");
ds.PropertiesToLoad.Add("distinguishedName"); //add at least one attribute so it doesn't return everything

var result = ds.FindOne();
var userDe = result.GetDirectoryEntry();

I notice you are also passing the username and password to PrincipalContext . If that's needed here, the constructor for DirectoryEntry does accept a username and password, so you can update this code to include that every time you create a new DirectoryEntry .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM