2

ユーザー名とパスワードを取得し、マシン上にユーザーを作成して2つの特定のグループに追加する次のコードがあります。グループパートに入ると、とても遅く、理由がわかりません。ログファイルによると、前回の実行では、ユーザーをUsersグループに追加するのに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も、私たちにとって非常に遅いものでした。LdapConnection解決策は、 fromを使用して低レベルのパスワード設定を使用する「ディレクトリサービスプログラミングの.NET開発者ガイド」のアプローチに従うことでしたSystem.DirectoryServices.Protocols

私たちが発見した最後のボトルネックは、User.GetGroups()メソッドが原因でした。

とにかく、ユーザーをグループに追加するためのコードがあなたに違いをもたらす場合は、メモを残してください。また、これを並行して実行する必要はないことにも注意してください。これがコードを高速化するためのアプローチであったことは理解していますが、実際には必要ありません。

于 2012-09-17T15:46:20.247 に答える