2

我有以下代码,它接受用户名和密码,然后在机器上创建用户并将它们添加到两个特定组中。当我进入小组部分时,它真的很慢,我不知道为什么。根据我的日志文件,我上次运行时说将用户添加到用户组需要 7 分钟,但 IIS_IUSRS 速度非常快。

下面是我的初始代码,它调用执行实际工作的方法。我曾尝试使用一项任务来帮助加快检查组的过程,但它的运行速度仍然非常慢。

public void Apply(Section.User.User user, Action<string> status)
    {
        #region Sanity Checks

        if (user == null)
        {
            throw new ArgumentNullException("user");
        }

        if (status == null)
        {
            throw new ArgumentNullException("status");
        }
        #endregion
        _logger.Debug(string.Format("Starting to apply the user with name {0}", user.UserName));
        status(string.Format("Applying User {0} to the system.", user.UserName));

        using (PrincipalContext pc = new PrincipalContext(ContextType.Machine))
        {

            UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(pc, user.UserName);
            try
            {
                _logger.Debug("Checking if user already exists");
                if (userPrincipal == null)
                {
                    userPrincipal = CreateNewUser(user, pc);
                }

                _logger.Debug("Setting user password and applying to the system.");
                userPrincipal.SetPassword(user.UserPassword);
                userPrincipal.Save();

                Task<PrincipalSearchResult<Principal>> groups =
                    Task<PrincipalSearchResult<Principal>>.Factory.StartNew(userPrincipal.GetGroups);

                _logger.Debug("Adding user to the groups.");
                AddUserToGroups(pc, userPrincipal, groups, user.UserType.Equals(UserType.WorkerProcess.ToString()) ? "Administrators" : "Users", "IIS_IUSRS");
                AddCurrentUser(user);
            }
            finally
            {
                if (userPrincipal != null)
                {
                    userPrincipal.Dispose();
                }
            }


        }

    }

如果用户不存在,这是我用来创建用户的私有方法。

private UserPrincipal CreateNewUser(Section.User.User user, PrincipalContext principal)
    {
        _logger.Debug("User did not exist creating now.");
        UserPrincipal newUser = new UserPrincipal(principal)
            {
                Name = user.UserName,
                Description = user.UserDescription,
                UserCannotChangePassword = false,
                PasswordNeverExpires = true,
                PasswordNotRequired = false
            };
        _logger.Debug("User created.");
        return newUser;
    }

以下是分组的逻辑。我在每次使用调试器时都会挂在的有问题的代码上方发表了评论。此外,调试日志条目也始终是我在挂起之前获得的最后一个条目。

private void AddUserToGroups(PrincipalContext principal, UserPrincipal user, Task<PrincipalSearchResult<Principal>> userGroups, params string[] groups)
    {
        groups.AsParallel().ForAll(s =>
            {
                using (GroupPrincipal gp = GroupPrincipal.FindByIdentity(principal, s))
                {
                    _logger.Debug(string.Format("Checking if user is alread in the group."));
                    if (gp != null && !userGroups.Result.Contains(gp))
                    {
                        _logger.Debug(string.Format("The user was not a member of {0} adding them now.", gp.Name));
                        //This is the point that the 7 minute hang starts
                        gp.Members.Add(user);
                        gp.Save();

                        _logger.Debug(string.Format("User added to {0}.", gp.Name));
                    }
                }
            });
    }

对此项目的任何帮助将不胜感激,因为该项目预计将于 10 月发布,但在创建用户时我无法在 7 分钟挂起的情况下发布。

4

1 回答 1

3

有同样的问题。看起来

    gp.Members.Add( user );

很慢,因为它首先枚举组(获取Members),然后才添加到集合中(这又增加了速度)。

解决方案是让它像:

    UserPrincipal user = this is your user;
    GroupPrincipal group = this is your group;

    // this is fast
    using ( DirectoryEntry groupEntry = group.GetUnderlyingObject() as DirectoryEntry )
    using ( DirectoryEntry userEntry = user.GetUnderlyingObject() as DirectoryEntry )
    {         
      groupEntry.Invoke( "Add", new object[] { userEntry.Path } ); 
    }

    //group.Members.Add(user); // and this is slow!
    //group.Save();

只是一个提示 - 创建密码SetPassword对我们来说也非常慢。解决方案是遵循“The .NET Developer's Guide to Directory Services Programming”中的方法,他们使用低级密码设置LdapConnectionfrom System.DirectoryServices.Protocols

我们发现的最后一个瓶颈是由User.GetGroups()方法引起的。

无论如何,如果将用户添加到组的代码对您有所帮助,请记下。另请注意,您实际上并不需要并行执行此操作 - 我知道这是您加快代码速度的方法,但您并不需要这样做。

于 2012-09-17T15:46:20.247 回答