批处理:尝试在 AAD 中异步添加多个用户时,userPrincipalName 已存在

batch processing: userPrincipalName already exists when trying to add multiple users async in AAD

我正在尝试使用 AAD 图异步添加多个用户,如下所示:

IUser user1 = ...;
IUser user2 = ...;
IUser user3 = ...;
List<Task> addTasks = new List<Task>();
addTasks.Add(activeDirectoryClient.Users.AddUserAsync(user1));
addTasks.Add(activeDirectoryClient.Users.AddUserAsync(user2));
addTasks.Add(activeDirectoryClient.Users.AddUserAsync(user3));
await Task.WhenAll(addTasks);

我收到此错误消息:

{"odata.error":{"code":"Request_BadRequest","message":{"lang":"en","value":"Another object with the same value for property userPrincipalName already exists."},"values":[{"item":"PropertyName","value":"userPrincipalName"},{"item":"PropertyErrorCode","value":"ObjectConflict"}]}}

如果我使用此代码:

IUser user1 = ...;
IUser user2 = ...;
IUser user3 = ...;
List<Task> addTasks = new List<Task>();
await activeDirectoryClient.Users.AddUserAsync(user1);
await activeDirectoryClient.Users.AddUserAsync(user2);
await activeDirectoryClient.Users.AddUserAsync(user3);

效果很好。

也许我们不能在 AAD 中同时添加多个用户?

编辑 这是用户部分:

IUser newStudentUser = new User
{
    DisplayName = $"Etudiant de l'école {school}",
    UserPrincipalName = $"etudiant-{school}@........fr",
    AccountEnabled = true,
    MailNickname = $"Etudiant {school}",
    UsageLocation = "US",
    PasswordProfile = new PasswordProfile
    {
        Password = "......."
    }
};
IUser newTeacherUser = new User
{
    DisplayName = $"Professeur de l'école {school}",
    UserPrincipalName = $"professeur-{school}@........fr",
    AccountEnabled = true,
    MailNickname = $"Professeur {school}",
    UsageLocation = "US",
    PasswordProfile = new PasswordProfile
    {
        Password = "......."
    }
};
IUser newDirectorUser = new User
{
    DisplayName = $"Directeur de l'école {school}",
    UserPrincipalName = $"directeur-{school}@........fr",
    AccountEnabled = true,
    MailNickname = $"Directeur {school}",
    UsageLocation = "US",
    PasswordProfile = new PasswordProfile
    {
        Password = "......."
    }
};

在您的情况下,您希望批量创建多个用户。

目前GraphClient确实支持批处理,但是有一些限制(Batch processing | Graph API concepts):

  • 单个批处理最多可以包含五个查询 and/or 组合的更改集。
  • 一个变更集最多可以包含一个源对象修改和最多 20 个添加-link 和删除-link 操作组合。更改集中的所有操作都必须在单个源实体上。

所以在你的情况下你不能批量创建超过 5 个用户

向 Graph API 客户端添加实体时,您可以选择使用 deferredSave 参数延迟执行查询。

await activeDirectoryClient.Users.AddUserAsync(user, deferredSave: true);

Graph API 客户端有一个跟踪变化的 DataServiceContextWrapper。它提供了一个 SaveChanges(Async) 方法。

await activeDirectoryClient.Context.SaveChangesAsync();

调用此方法可以指定SaveChangesOptions:

  • 批处理:所有待定更改都保存在单个批处理请求中。
  • BatchWithIndependentOperations:您要使用的那个,因为用户的创建应该在单个查询中。
  • ContinueOnError:使用对服务器的多次请求保存未决更改,发生错误后操作继续。
  • None:通过向服务器多次请求保存未决更改,但操作在第一次失败时停止(默认)。
  • 补丁更新:
  • ReplaceOnUpdate:通过将数据源中实体的所有值替换为已更新实体的值来进行待定更新。

现在您有足够的信息来编写代码来批量创建用户:

// Only 5 users per batch !!!!
var user1 = ...;
var user2 = ...;
var user3 = ...;
await activeDirectoryClient.Users.AddUserAsync(newStudentUser, deferredSave: true);
await activeDirectoryClient.Users.AddUserAsync(newTeacherUser, deferredSave: true);
await activeDirectoryClient.Users.AddUserAsync(newDirectorUser, deferredSave: true);

// In debug mode, you should use the SaveChangesAsync method with the default options
// Becasue the BatchWithIndependentOperations will not throw any exception even if there is a problem while creating the user.
//await activeDirectoryClient.Context.SaveChangesAsync();
await activeDirectoryClient.Context
    .SaveChangesAsync(SaveChangesOptions.BatchWithIndependentOperations);