From 7e76587f601f879839cf9bb932079edf8e1dc06f Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 10 Oct 2025 13:49:47 +0800 Subject: [PATCH] Add batch retrieval for IdentityLinkUser entities --- .../Identity/IIdentityLinkUserRepository.cs | 4 ++ .../Abp/Identity/IdentityLinkUserManager.cs | 72 +++++++++++-------- .../EfCoreIdentityLinkUserRepository.cs | 22 ++++++ .../MongoIdentityLinkUserRepository.cs | 21 ++++++ 4 files changed, 89 insertions(+), 30 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityLinkUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityLinkUserRepository.cs index fb96806b9bb..520eb1c0fcb 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityLinkUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityLinkUserRepository.cs @@ -18,6 +18,10 @@ Task> GetListAsync( List excludes = null, CancellationToken cancellationToken = default); + Task> GetListAsync( + int batchSize, + CancellationToken cancellationToken = default); + Task DeleteAsync( IdentityLinkUserInfo linkUserInfo, CancellationToken cancellationToken = default); diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityLinkUserManager.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityLinkUserManager.cs index 6c7de47dc09..19886bdd3a8 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityLinkUserManager.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityLinkUserManager.cs @@ -1,6 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; -using System.Text.Encodings.Web; using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Services; @@ -23,52 +23,64 @@ public IdentityLinkUserManager(IIdentityLinkUserRepository identityLinkUserRepos CurrentTenant = currentTenant; } - public async Task> GetListAsync(IdentityLinkUserInfo linkUserInfo, bool includeIndirect = false, CancellationToken cancellationToken = default) + public async Task> GetListAsync(IdentityLinkUserInfo linkUserInfo, bool includeIndirect = false, int batchSize = 100 * 100, CancellationToken cancellationToken = default) { using (CurrentTenant.Change(null)) { var users = await IdentityLinkUserRepository.GetListAsync(linkUserInfo, cancellationToken: cancellationToken); - if (includeIndirect == false) + if (!includeIndirect) { return users; } - var userInfos = new List() - { - linkUserInfo - }; + var allUsers = await IdentityLinkUserRepository.GetListAsync(batchSize ,cancellationToken: cancellationToken); + return await GetAllRelatedLinksAsync(allUsers, linkUserInfo); + } + } + + protected virtual Task> GetAllRelatedLinksAsync(List allUsers, IdentityLinkUserInfo userInfo) + { + var visited = new HashSet<(Guid, Guid?)>(); + var result = new List(); + var queue = new Queue<(Guid, Guid?)>(); + + queue.Enqueue((userInfo.UserId, userInfo.TenantId)); + visited.Add((userInfo.UserId, userInfo.TenantId)); + + while (queue.Count > 0) + { + var (currentUserId, currentTenantId) = queue.Dequeue(); - var allUsers = new List(); - allUsers.AddRange(users); + var relatedLinks = allUsers.Where(x => + (x.SourceUserId == currentUserId && x.SourceTenantId == currentTenantId) || + (x.TargetUserId == currentUserId && x.TargetTenantId == currentTenantId) + ).ToList(); - do + foreach (var link in relatedLinks) { - var nextUsers = new List(); - foreach (var user in users) + var node1 = (link.SourceUserId, link.SourceTenantId); + var node2 = (link.TargetUserId, link.TargetTenantId); + + if (!result.Contains(link)) { - if (userInfos.Any(x => x.TenantId != user.SourceTenantId || x.UserId != user.SourceUserId)) - { - nextUsers.Add(new IdentityLinkUserInfo(user.SourceUserId, user.SourceTenantId)); - } - - if (userInfos.Any(x => x.TenantId != user.TargetTenantId || x.UserId != user.TargetUserId)) - { - nextUsers.Add(new IdentityLinkUserInfo(user.TargetUserId, user.TargetTenantId)); - } + result.Add(link); } - users = new List(); - foreach (var next in nextUsers) + if (!visited.Contains(node1)) { - users.AddRange(await IdentityLinkUserRepository.GetListAsync(next, userInfos, cancellationToken)); + queue.Enqueue(node1); + visited.Add(node1); } - userInfos.AddRange(nextUsers); - allUsers.AddRange(users); - } while (users.Any()); - - return allUsers; + if (!visited.Contains(node2)) + { + queue.Enqueue(node2); + visited.Add(node2); + } + } } + + return Task.FromResult(result); } public virtual async Task LinkAsync(IdentityLinkUserInfo sourceLinkUser, IdentityLinkUserInfo targetLinkUser, CancellationToken cancellationToken = default) diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityLinkUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityLinkUserRepository.cs index 035904120c4..0103f445c7d 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityLinkUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityLinkUserRepository.cs @@ -51,6 +51,28 @@ public virtual async Task> GetListAsync(IdentityLinkUserI return await query.ToListAsync(cancellationToken: GetCancellationToken(cancellationToken)); } + public virtual async Task> GetListAsync(int batchSize, CancellationToken cancellationToken = default) + { + var result = new List(); + + var total = await (await GetDbSetAsync()).LongCountAsync(cancellationToken); + var pages = (int)Math.Ceiling(total / (double)batchSize); + + for (var page = 0; page < pages; page++) + { + var batch = await (await GetDbSetAsync()) + .AsNoTracking() + .OrderBy(x => x.Id) + .Skip(page * batchSize) + .Take(batchSize) + .ToListAsync(cancellationToken); + + result.AddRange(batch); + } + + return result; + } + public virtual async Task DeleteAsync(IdentityLinkUserInfo linkUserInfo, CancellationToken cancellationToken = default) { var linkUsers = await (await GetDbSetAsync()).AsNoTracking().Where(x => diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityLinkUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityLinkUserRepository.cs index 46cfc60eb30..e28fe049cd9 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityLinkUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityLinkUserRepository.cs @@ -47,6 +47,27 @@ public virtual async Task> GetListAsync(IdentityLinkUserI return await query.ToListAsync(cancellationToken: GetCancellationToken(cancellationToken)); } + public virtual async Task> GetListAsync(int batchSize, CancellationToken cancellationToken = default) + { + var result = new List(); + + var total = await (await GetQueryableAsync(cancellationToken)).LongCountAsync(cancellationToken); + var pages = (int)Math.Ceiling(total / (double)batchSize); + + for (var page = 0; page < pages; page++) + { + var batch = await (await GetQueryableAsync(cancellationToken)) + .OrderBy(x => x.Id) + .Skip(page * batchSize) + .Take(batchSize) + .ToListAsync(cancellationToken); + + result.AddRange(batch); + } + + return result; + } + public virtual async Task DeleteAsync(IdentityLinkUserInfo linkUserInfo, CancellationToken cancellationToken = default) { var linkUsers = await (await GetQueryableAsync(cancellationToken)).Where(x =>