繁体   English   中英

PrincipalSearchResult和System.OutOfMemoryException

[英]PrincipalSearchResult and System.OutOfMemoryException

我正在使用Domain PrincipalContext查找用户组。 我明白了。 但是,当我尝试使用组集合时,会出现System.OutOfMemoryException 所有主要对象都是一次性的。 在我的代码中有using部分。 我曾尝试使用Dispose()方法和GC.Collect()但这无济于事。

这是代码:

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循环返回异常。 甚至foreach都是空循环。

我发现System.DirectoryServices.AccountManagement命名空间( UserPrincipal等)确实浪费了很多内存。 例如,每次创建UserPrincipalGroupPrincipal ,它都会询问AD每个具有值的属性-即使您只使用了其中一个。

如果用户是许多组的成员,则可能是原因,尽管我仍然感到惊讶。 也许您的计算机没有可用的内存来加载所有这些。

您可以通过直接使用System.DirectoryServices命名空间(这也是AccountManagement命名空间在后台使用的功能)来执行相同的操作并使用更少的内存(可能花费更少的时间)。

这是一个示例,它将查看用户的memberOf属性以查找组并拉出Guid 这确实有一些限制,在此我描述了我写的一篇文章: 查找用户的所有组 (此示例从该文章中的一个进行了修改)。 但是,在大多数情况下(特别是如果您的环境中只有一个域而没有受信任的域),就可以了。

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;
}

您需要向其提供用户的DirectoryEntry对象。 如果您事先知道distinguishedName ,则可以使用它(例如new DirectoryEntry($"LDAP://{distinguishedName}") )。 但是,如果没有,您可以搜索它:

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();

我注意到您还将用户名和密码传递给PrincipalContext 如果此处需要这样做,则DirectoryEntry构造函数会接受用户名和密码,因此您可以在每次创建新的DirectoryEntry时更新此代码,以包括该代码。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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