From 495db440615c7306193a5f63db76dce7324642d6 Mon Sep 17 00:00:00 2001 From: Mansur Besleney Date: Thu, 16 Jan 2025 14:20:06 +0300 Subject: [PATCH 01/10] id field added to filter user list by id --- .../Abp/Identity/IIdentityUserRepository.cs | 2 + .../EfCoreIdentityUserRepository.cs | 56 ++++++++++--------- .../MongoDB/MongoIdentityUserRepository.cs | 21 ++++--- 3 files changed, 46 insertions(+), 33 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs index f17f74b602..a79fe2dfe3 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs @@ -69,6 +69,7 @@ public interface IIdentityUserRepository : IBasicRepository bool includeDetails = false, Guid? roleId = null, Guid? organizationUnitId = null, + string id = null, string userName = null, string phoneNumber = null, string emailAddress = null, @@ -114,6 +115,7 @@ public interface IIdentityUserRepository : IBasicRepository string filter = null, Guid? roleId = null, Guid? organizationUnitId = null, + string id = null, string userName = null, string phoneNumber = null, string emailAddress = null, diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs index 86682b85db..d966af480d 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs @@ -62,35 +62,35 @@ public class EfCoreIdentityUserRepository : EfCoreRepository() - join role in dbContext.Roles on userRole.RoleId equals role.Id - where userIds.Contains(userRole.UserId) - group new - { - userRole.UserId, - role.Name - } by userRole.UserId + join role in dbContext.Roles on userRole.RoleId equals role.Id + where userIds.Contains(userRole.UserId) + group new { + userRole.UserId, + role.Name + } by userRole.UserId into gp - select new IdentityUserIdWithRoleNames - { - Id = gp.Key, RoleNames = gp.Select(x => x.Name).ToArray() - }).ToListAsync(cancellationToken: cancellationToken); + select new IdentityUserIdWithRoleNames + { + Id = gp.Key, + RoleNames = gp.Select(x => x.Name).ToArray() + }).ToListAsync(cancellationToken: cancellationToken); var orgUnitRoles = await (from userOu in dbContext.Set() - join roleOu in dbContext.Set() on userOu.OrganizationUnitId equals roleOu.OrganizationUnitId - join role in dbContext.Roles on roleOu.RoleId equals role.Id - where userIds.Contains(userOu.UserId) - group new - { - userOu.UserId, - role.Name - } by userOu.UserId + join roleOu in dbContext.Set() on userOu.OrganizationUnitId equals roleOu.OrganizationUnitId + join role in dbContext.Roles on roleOu.RoleId equals role.Id + where userIds.Contains(userOu.UserId) + group new { + userOu.UserId, + role.Name + } by userOu.UserId into gp - select new IdentityUserIdWithRoleNames - { - Id = gp.Key, RoleNames = gp.Select(x => x.Name).ToArray() - }).ToListAsync(cancellationToken: cancellationToken); + select new IdentityUserIdWithRoleNames + { + Id = gp.Key, + RoleNames = gp.Select(x => x.Name).ToArray() + }).ToListAsync(cancellationToken: cancellationToken); - return userRoles.Concat(orgUnitRoles).GroupBy(x => x.Id).Select(x => new IdentityUserIdWithRoleNames {Id = x.Key, RoleNames = x.SelectMany(y => y.RoleNames).Distinct().ToArray()}).ToList(); + return userRoles.Concat(orgUnitRoles).GroupBy(x => x.Id).Select(x => new IdentityUserIdWithRoleNames { Id = x.Key, RoleNames = x.SelectMany(y => y.RoleNames).Distinct().ToArray() }).ToList(); } public virtual async Task> GetRoleNamesInOrganizationUnitAsync( @@ -196,6 +196,7 @@ public class EfCoreIdentityUserRepository : EfCoreRepository p.CreationTime <= maxCreationTime) .WhereIf(minCreationTime != null, p => p.CreationTime >= minCreationTime) .WhereIf(maxModifitionTime != null, p => p.LastModificationTime <= maxModifitionTime) - .WhereIf(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime); + .WhereIf(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime) + .WhereIf(!string.IsNullOrWhiteSpace(id), x => x.Id.ToString() == id); } } diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs index 58486893ec..35ae368e80 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs @@ -164,6 +164,7 @@ public class MongoIdentityUserRepository : MongoDbRepository x.Value); var organizationUnitAndRoleIds = await (await GetMongoQueryableAsync(cancellationToken)).Where(ou => organizationUnitIds.Contains(ou.Id)) - .Select(userOrganizationUnit => new - { + .Select(userOrganizationUnit => new { userOrganizationUnit.Id, userOrganizationUnit.Roles }).ToListAsync(cancellationToken: cancellationToken); var allOrganizationUnitRoleIds = organizationUnitAndRoleIds.SelectMany(x => x.Roles.Select(r => r.RoleId)).ToList(); var allRoleIds = roleIds.Union(allOrganizationUnitRoleIds); - var roles = await (await GetMongoQueryableAsync(cancellationToken)).Where(r => allRoleIds.Contains(r.Id)).Select(r => new{ r.Id, r.Name }).ToListAsync(cancellationToken); + var roles = await (await GetMongoQueryableAsync(cancellationToken)).Where(r => allRoleIds.Contains(r.Id)).Select(r => new { r.Id, r.Name }).ToListAsync(cancellationToken); var userRoles = userAndRoleIds.ToDictionary(x => x.Key, x => roles.Where(r => x.Value.Contains(r.Id)).Select(r => r.Name).ToArray()); var result = userRoles.Select(x => new IdentityUserIdWithRoleNames { Id = x.Key, RoleNames = x.Value }).ToList(); @@ -413,9 +416,9 @@ public class MongoIdentityUserRepository : MongoDbRepository identityUser.Roles.Any(x => x.RoleId == roleId.Value) || identityUser.OrganizationUnits.Any(x => organizationUnitIds.Contains(x.OrganizationUnitId))); } - return query + return query .WhereIf>( !filter.IsNullOrWhiteSpace(), u => @@ -471,13 +475,14 @@ public class MongoIdentityUserRepository : MongoDbRepository>(!string.IsNullOrWhiteSpace(name), x => x.Name == name) .WhereIf>(!string.IsNullOrWhiteSpace(surname), x => x.Surname == surname) .WhereIf>(isLockedOut.HasValue && isLockedOut.Value, x => x.LockoutEnabled && x.LockoutEnd != null && x.LockoutEnd > DateTimeOffset.UtcNow) - .WhereIf>(isLockedOut.HasValue && !isLockedOut.Value, x => !(x.LockoutEnabled && x.LockoutEnd != null && x.LockoutEnd > DateTimeOffset.UtcNow)) + .WhereIf>(isLockedOut.HasValue && !isLockedOut.Value, x => !(x.LockoutEnabled && x.LockoutEnd != null && x.LockoutEnd > DateTimeOffset.UtcNow)) .WhereIf>(notActive.HasValue, x => x.IsActive == !notActive.Value) .WhereIf>(emailConfirmed.HasValue, x => x.EmailConfirmed == emailConfirmed.Value) .WhereIf>(isExternal.HasValue, x => x.IsExternal == isExternal.Value) .WhereIf>(maxCreationTime != null, p => p.CreationTime <= maxCreationTime) .WhereIf>(minCreationTime != null, p => p.CreationTime >= minCreationTime) .WhereIf>(maxModifitionTime != null, p => p.LastModificationTime <= maxModifitionTime) - .WhereIf>(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime); + .WhereIf>(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime) + .WhereIf>(!string.IsNullOrWhiteSpace(id), x => x.Id.ToString() == id); } } From 85ee5d78d40704d2b268e594b584cb5ddea78cbc Mon Sep 17 00:00:00 2001 From: Mansur Besleney Date: Thu, 16 Jan 2025 16:03:00 +0300 Subject: [PATCH 02/10] string type correcred to guid --- .../Volo/Abp/Identity/IIdentityUserRepository.cs | 4 ++-- .../EntityFrameworkCore/EfCoreIdentityUserRepository.cs | 8 ++++---- .../Abp/Identity/MongoDB/MongoIdentityUserRepository.cs | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs index a79fe2dfe3..8da3237f83 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IIdentityUserRepository.cs @@ -69,7 +69,7 @@ public interface IIdentityUserRepository : IBasicRepository bool includeDetails = false, Guid? roleId = null, Guid? organizationUnitId = null, - string id = null, + Guid? id = null, string userName = null, string phoneNumber = null, string emailAddress = null, @@ -115,7 +115,7 @@ public interface IIdentityUserRepository : IBasicRepository string filter = null, Guid? roleId = null, Guid? organizationUnitId = null, - string id = null, + Guid? id = null, string userName = null, string phoneNumber = null, string emailAddress = null, diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs index d966af480d..eb20f26102 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs @@ -196,7 +196,7 @@ public class EfCoreIdentityUserRepository : EfCoreRepository p.CreationTime >= minCreationTime) .WhereIf(maxModifitionTime != null, p => p.LastModificationTime <= maxModifitionTime) .WhereIf(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime) - .WhereIf(!string.IsNullOrWhiteSpace(id), x => x.Id.ToString() == id); + .WhereIf(id.HasValue, x => x.Id == id); } } diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs index 35ae368e80..736baa69bf 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs @@ -164,7 +164,7 @@ public class MongoIdentityUserRepository : MongoDbRepository>(minCreationTime != null, p => p.CreationTime >= minCreationTime) .WhereIf>(maxModifitionTime != null, p => p.LastModificationTime <= maxModifitionTime) .WhereIf>(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime) - .WhereIf>(!string.IsNullOrWhiteSpace(id), x => x.Id.ToString() == id); + .WhereIf>(id.HasValue, x => x.Id == id); } } From 38393174c6d02f1a717d80355b01be8eb60e617a Mon Sep 17 00:00:00 2001 From: Mansur Besleney Date: Thu, 23 Jan 2025 16:32:10 +0300 Subject: [PATCH 03/10] Update MongoIdentityUserRepository.cs --- .../Identity/MongoDB/MongoIdentityUserRepository.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs index 736baa69bf..16aa64314d 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs @@ -395,14 +395,15 @@ public class MongoIdentityUserRepository : MongoDbRepository x.Value); var organizationUnitAndRoleIds = await (await GetMongoQueryableAsync(cancellationToken)).Where(ou => organizationUnitIds.Contains(ou.Id)) - .Select(userOrganizationUnit => new { + .Select(userOrganizationUnit => new + { userOrganizationUnit.Id, userOrganizationUnit.Roles }).ToListAsync(cancellationToken: cancellationToken); var allOrganizationUnitRoleIds = organizationUnitAndRoleIds.SelectMany(x => x.Roles.Select(r => r.RoleId)).ToList(); var allRoleIds = roleIds.Union(allOrganizationUnitRoleIds); - var roles = await (await GetMongoQueryableAsync(cancellationToken)).Where(r => allRoleIds.Contains(r.Id)).Select(r => new { r.Id, r.Name }).ToListAsync(cancellationToken); + var roles = await (await GetMongoQueryableAsync(cancellationToken)).Where(r => allRoleIds.Contains(r.Id)).Select(r => new{ r.Id, r.Name }).ToListAsync(cancellationToken); var userRoles = userAndRoleIds.ToDictionary(x => x.Key, x => roles.Where(r => x.Value.Contains(r.Id)).Select(r => r.Name).ToArray()); var result = userRoles.Select(x => new IdentityUserIdWithRoleNames { Id = x.Key, RoleNames = x.Value }).ToList(); @@ -416,9 +417,9 @@ public class MongoIdentityUserRepository : MongoDbRepository>(!string.IsNullOrWhiteSpace(name), x => x.Name == name) .WhereIf>(!string.IsNullOrWhiteSpace(surname), x => x.Surname == surname) .WhereIf>(isLockedOut.HasValue && isLockedOut.Value, x => x.LockoutEnabled && x.LockoutEnd != null && x.LockoutEnd > DateTimeOffset.UtcNow) - .WhereIf>(isLockedOut.HasValue && !isLockedOut.Value, x => !(x.LockoutEnabled && x.LockoutEnd != null && x.LockoutEnd > DateTimeOffset.UtcNow)) + .WhereIf>(isLockedOut.HasValue && !isLockedOut.Value, x => !(x.LockoutEnabled && x.LockoutEnd != null && x.LockoutEnd > DateTimeOffset.UtcNow)) .WhereIf>(notActive.HasValue, x => x.IsActive == !notActive.Value) .WhereIf>(emailConfirmed.HasValue, x => x.EmailConfirmed == emailConfirmed.Value) .WhereIf>(isExternal.HasValue, x => x.IsExternal == isExternal.Value) From 2f67c20f7d9de67fc2fd9b61fd8a2ab609ffdce4 Mon Sep 17 00:00:00 2001 From: Mansur Besleney Date: Thu, 23 Jan 2025 17:02:21 +0300 Subject: [PATCH 04/10] Update MongoIdentityUserRepository.cs --- .../MongoDB/MongoIdentityUserRepository.cs | 102 +++++++++--------- 1 file changed, 50 insertions(+), 52 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs index 16aa64314d..535e058540 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs @@ -25,7 +25,7 @@ public class MongoIdentityUserRepository : MongoDbRepository x.Id) .FirstOrDefaultAsync( u => u.NormalizedUserName == normalizedUserName, @@ -43,13 +43,13 @@ public class MongoIdentityUserRepository : MongoDbRepository r.OrganizationUnitId) .ToArray(); - var organizationUnits = await (await GetMongoQueryableAsync(cancellationToken)) + var organizationUnits = await (await GetQueryableAsync(cancellationToken)) .Where(ou => organizationUnitIds.Contains(ou.Id)) .ToListAsync(cancellationToken: cancellationToken); var orgUnitRoleIds = organizationUnits.SelectMany(x => x.Roles.Select(r => r.RoleId)).ToArray(); var roleIds = user.Roles.Select(r => r.RoleId).ToArray(); var allRoleIds = orgUnitRoleIds.Union(roleIds); - return await (await GetMongoQueryableAsync(cancellationToken)).Where(r => allRoleIds.Contains(r.Id)).Select(r => r.Name).ToListAsync(cancellationToken); + return await (await GetQueryableAsync(cancellationToken)).Where(r => allRoleIds.Contains(r.Id)).Select(r => r.Name).ToListAsync(cancellationToken); } public virtual async Task> GetRoleNamesInOrganizationUnitAsync( @@ -63,13 +63,13 @@ public class MongoIdentityUserRepository : MongoDbRepository r.OrganizationUnitId) .ToArray(); - var organizationUnits = await (await GetMongoQueryableAsync(cancellationToken)) + var organizationUnits = await (await GetQueryableAsync(cancellationToken)) .Where(ou => organizationUnitIds.Contains(ou.Id)) .ToListAsync(cancellationToken: cancellationToken); var roleIds = organizationUnits.SelectMany(x => x.Roles.Select(r => r.RoleId)).ToArray(); - var queryable = await GetMongoQueryableAsync(cancellationToken); + var queryable = await GetQueryableAsync(cancellationToken); return await queryable .Where(r => roleIds.Contains(r.Id)) @@ -83,7 +83,7 @@ public class MongoIdentityUserRepository : MongoDbRepository u.Logins.Any(login => login.LoginProvider == loginProvider && login.ProviderKey == providerKey)) .OrderBy(x => x.Id) .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); @@ -94,7 +94,7 @@ public class MongoIdentityUserRepository : MongoDbRepository x.Id).FirstOrDefaultAsync(u => u.NormalizedEmail == normalizedEmail, GetCancellationToken(cancellationToken)); } @@ -103,14 +103,14 @@ public class MongoIdentityUserRepository : MongoDbRepository u.Claims.Any(c => c.ClaimType == claim.Type && c.ClaimValue == claim.Value)) .ToListAsync(GetCancellationToken(cancellationToken)); } public virtual async Task RemoveClaimFromAllUsersAsync(string claimType, bool autoSave, CancellationToken cancellationToken = default) { - var users = await (await GetMongoQueryableAsync(cancellationToken)) + var users = await (await GetQueryableAsync(cancellationToken)) .Where(u => u.Claims.Any(c => c.ClaimType == claimType)) .ToListAsync(GetCancellationToken(cancellationToken)); @@ -129,7 +129,7 @@ public class MongoIdentityUserRepository : MongoDbRepository(cancellationToken); + var queryable = await GetQueryableAsync(cancellationToken); var role = await queryable .Where(x => x.NormalizedName == normalizedRoleName) @@ -141,7 +141,7 @@ public class MongoIdentityUserRepository : MongoDbRepository(); } - return await (await GetMongoQueryableAsync(cancellationToken)) + return await (await GetQueryableAsync(cancellationToken)) .Where(u => u.Roles.Any(r => r.RoleId == role.Id)) .ToListAsync(cancellationToken); } @@ -150,7 +150,7 @@ public class MongoIdentityUserRepository : MongoDbRepository u.Roles.Any(r => r.RoleId == roleId)) .Select(x => x.Id) .ToListAsync(cancellationToken); @@ -203,8 +203,7 @@ public class MongoIdentityUserRepository : MongoDbRepository>() - .PageBy>(skipCount, maxResultCount) + .PageBy(skipCount, maxResultCount) .ToListAsync(GetCancellationToken(cancellationToken)); } @@ -219,13 +218,13 @@ public class MongoIdentityUserRepository : MongoDbRepository r.OrganizationUnitId) .ToArray(); - var organizationUnits = await (await GetMongoQueryableAsync(cancellationToken)) + var organizationUnits = await (await GetQueryableAsync(cancellationToken)) .Where(ou => organizationUnitIds.Contains(ou.Id)) .ToListAsync(cancellationToken: cancellationToken); var orgUnitRoleIds = organizationUnits.SelectMany(x => x.Roles.Select(r => r.RoleId)).ToArray(); var roleIds = user.Roles.Select(r => r.RoleId).ToArray(); var allRoleIds = orgUnitRoleIds.Union(roleIds); - return await (await GetMongoQueryableAsync(cancellationToken)).Where(r => allRoleIds.Contains(r.Id)).ToListAsync(cancellationToken); + return await (await GetQueryableAsync(cancellationToken)).Where(r => allRoleIds.Contains(r.Id)).ToListAsync(cancellationToken); } public virtual async Task> GetOrganizationUnitsAsync( @@ -237,7 +236,7 @@ public class MongoIdentityUserRepository : MongoDbRepository r.OrganizationUnitId); - return await (await GetMongoQueryableAsync(cancellationToken)) + return await (await GetQueryableAsync(cancellationToken)) .Where(ou => organizationUnitIds.Contains(ou.Id)) .ToListAsync(cancellationToken); } @@ -290,7 +289,7 @@ public class MongoIdentityUserRepository : MongoDbRepository u.OrganizationUnits.Any(uou => uou.OrganizationUnitId == organizationUnitId)) .ToListAsync(GetCancellationToken(cancellationToken)); return result; @@ -300,7 +299,7 @@ public class MongoIdentityUserRepository : MongoDbRepository organizationUnitIds, CancellationToken cancellationToken = default) { - var result = await (await GetMongoQueryableAsync(cancellationToken)) + var result = await (await GetQueryableAsync(cancellationToken)) .Where(u => u.OrganizationUnits.Any(uou => organizationUnitIds.Contains(uou.OrganizationUnitId))) .ToListAsync(GetCancellationToken(cancellationToken)); return result; @@ -312,12 +311,12 @@ public class MongoIdentityUserRepository : MongoDbRepository(cancellationToken)) + var organizationUnitIds = await (await GetQueryableAsync(cancellationToken)) .Where(ou => ou.Code.StartsWith(code)) .Select(ou => ou.Id) .ToListAsync(cancellationToken); - return await (await GetMongoQueryableAsync(cancellationToken)) + return await (await GetQueryableAsync(cancellationToken)) .Where(u => u.OrganizationUnits.Any(uou => organizationUnitIds.Contains(uou.OrganizationUnitId))) .ToListAsync(cancellationToken); } @@ -328,7 +327,7 @@ public class MongoIdentityUserRepository : MongoDbRepository u.TenantId == tenantId && u.UserName == userName, GetCancellationToken(cancellationToken) @@ -337,14 +336,14 @@ public class MongoIdentityUserRepository : MongoDbRepository> GetListByIdsAsync(IEnumerable ids, bool includeDetails = false, CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync(cancellationToken)) + return await (await GetQueryableAsync(cancellationToken)) .Where(x => ids.Contains(x.Id)) .ToListAsync(GetCancellationToken(cancellationToken)); } public virtual async Task UpdateRoleAsync(Guid sourceRoleId, Guid? targetRoleId, CancellationToken cancellationToken = default) { - var users = await (await GetMongoQueryableAsync(cancellationToken)) + var users = await (await GetQueryableAsync(cancellationToken)) .Where(x => x.Roles.Any(r => r.RoleId == sourceRoleId)) .ToListAsync(GetCancellationToken(cancellationToken)); @@ -362,7 +361,7 @@ public class MongoIdentityUserRepository : MongoDbRepository x.OrganizationUnits.Any(r => r.OrganizationUnitId == sourceOrganizationId)) .ToListAsync(GetCancellationToken(cancellationToken)); @@ -394,16 +393,15 @@ public class MongoIdentityUserRepository : MongoDbRepository x.Value); var roleIds = userAndRoleIds.SelectMany(x => x.Value); - var organizationUnitAndRoleIds = await (await GetMongoQueryableAsync(cancellationToken)).Where(ou => organizationUnitIds.Contains(ou.Id)) - .Select(userOrganizationUnit => new - { + var organizationUnitAndRoleIds = await (await GetQueryableAsync(cancellationToken)).Where(ou => organizationUnitIds.Contains(ou.Id)) + .Select(userOrganizationUnit => new { userOrganizationUnit.Id, userOrganizationUnit.Roles }).ToListAsync(cancellationToken: cancellationToken); var allOrganizationUnitRoleIds = organizationUnitAndRoleIds.SelectMany(x => x.Roles.Select(r => r.RoleId)).ToList(); var allRoleIds = roleIds.Union(allOrganizationUnitRoleIds); - var roles = await (await GetMongoQueryableAsync(cancellationToken)).Where(r => allRoleIds.Contains(r.Id)).Select(r => new{ r.Id, r.Name }).ToListAsync(cancellationToken); + var roles = await (await GetQueryableAsync(cancellationToken)).Where(r => allRoleIds.Contains(r.Id)).Select(r => new { r.Id, r.Name }).ToListAsync(cancellationToken); var userRoles = userAndRoleIds.ToDictionary(x => x.Key, x => roles.Where(r => x.Value.Contains(r.Id)).Select(r => r.Name).ToArray()); var result = userRoles.Select(x => new IdentityUserIdWithRoleNames { Id = x.Key, RoleNames = x.Value }).ToList(); @@ -417,16 +415,16 @@ public class MongoIdentityUserRepository : MongoDbRepository> GetFilteredQueryableAsync( + protected virtual async Task> GetFilteredQueryableAsync( string filter = null, Guid? roleId = null, Guid? organizationUnitId = null, @@ -447,11 +445,11 @@ public class MongoIdentityUserRepository : MongoDbRepository(cancellationToken)) + var organizationUnitIds = (await GetQueryableAsync(cancellationToken)) .Where(ou => ou.Roles.Any(r => r.RoleId == roleId.Value)) .Select(userOrganizationUnit => userOrganizationUnit.Id) .ToArray(); @@ -460,7 +458,7 @@ public class MongoIdentityUserRepository : MongoDbRepository>( + .WhereIf( !filter.IsNullOrWhiteSpace(), u => u.NormalizedUserName.Contains(upperFilter) || @@ -469,21 +467,21 @@ public class MongoIdentityUserRepository : MongoDbRepository>(organizationUnitId.HasValue, identityUser => identityUser.OrganizationUnits.Any(x => x.OrganizationUnitId == organizationUnitId.Value)) - .WhereIf>(!string.IsNullOrWhiteSpace(userName), x => x.UserName == userName) - .WhereIf>(!string.IsNullOrWhiteSpace(phoneNumber), x => x.PhoneNumber == phoneNumber) - .WhereIf>(!string.IsNullOrWhiteSpace(emailAddress), x => x.Email == emailAddress) - .WhereIf>(!string.IsNullOrWhiteSpace(name), x => x.Name == name) - .WhereIf>(!string.IsNullOrWhiteSpace(surname), x => x.Surname == surname) - .WhereIf>(isLockedOut.HasValue && isLockedOut.Value, x => x.LockoutEnabled && x.LockoutEnd != null && x.LockoutEnd > DateTimeOffset.UtcNow) - .WhereIf>(isLockedOut.HasValue && !isLockedOut.Value, x => !(x.LockoutEnabled && x.LockoutEnd != null && x.LockoutEnd > DateTimeOffset.UtcNow)) - .WhereIf>(notActive.HasValue, x => x.IsActive == !notActive.Value) - .WhereIf>(emailConfirmed.HasValue, x => x.EmailConfirmed == emailConfirmed.Value) - .WhereIf>(isExternal.HasValue, x => x.IsExternal == isExternal.Value) - .WhereIf>(maxCreationTime != null, p => p.CreationTime <= maxCreationTime) - .WhereIf>(minCreationTime != null, p => p.CreationTime >= minCreationTime) - .WhereIf>(maxModifitionTime != null, p => p.LastModificationTime <= maxModifitionTime) - .WhereIf>(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime) - .WhereIf>(id.HasValue, x => x.Id == id); + .WhereIf(organizationUnitId.HasValue, identityUser => identityUser.OrganizationUnits.Any(x => x.OrganizationUnitId == organizationUnitId.Value)) + .WhereIf(!string.IsNullOrWhiteSpace(userName), x => x.UserName == userName) + .WhereIf(!string.IsNullOrWhiteSpace(phoneNumber), x => x.PhoneNumber == phoneNumber) + .WhereIf(!string.IsNullOrWhiteSpace(emailAddress), x => x.Email == emailAddress) + .WhereIf(!string.IsNullOrWhiteSpace(name), x => x.Name == name) + .WhereIf(!string.IsNullOrWhiteSpace(surname), x => x.Surname == surname) + .WhereIf(isLockedOut.HasValue && isLockedOut.Value, x => x.LockoutEnabled && x.LockoutEnd != null && x.LockoutEnd > DateTimeOffset.UtcNow) + .WhereIf(isLockedOut.HasValue && !isLockedOut.Value, x => !(x.LockoutEnabled && x.LockoutEnd != null && x.LockoutEnd > DateTimeOffset.UtcNow)) + .WhereIf(notActive.HasValue, x => x.IsActive == !notActive.Value) + .WhereIf(emailConfirmed.HasValue, x => x.EmailConfirmed == emailConfirmed.Value) + .WhereIf(isExternal.HasValue, x => x.IsExternal == isExternal.Value) + .WhereIf(maxCreationTime != null, p => p.CreationTime <= maxCreationTime) + .WhereIf(minCreationTime != null, p => p.CreationTime >= minCreationTime) + .WhereIf(maxModifitionTime != null, p => p.LastModificationTime <= maxModifitionTime) + .WhereIf(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime) + .WhereIf(id.HasValue, x => x.Id == id); } -} +} \ No newline at end of file From 7e8c7aba45e52ee2a030f06c0602bf97a783bdda Mon Sep 17 00:00:00 2001 From: Mansur Besleney Date: Fri, 24 Jan 2025 11:28:30 +0300 Subject: [PATCH 05/10] made slight query enhancment --- .../EntityFrameworkCore/EfCoreIdentityUserRepository.cs | 8 ++++++-- .../Abp/Identity/MongoDB/MongoIdentityUserRepository.cs | 7 +++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs index eb20f26102..002261df46 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs @@ -464,6 +464,11 @@ public class EfCoreIdentityUserRepository : EfCoreRepository identityUser.Roles.Any(x => x.RoleId == roleId.Value) || identityUser.OrganizationUnits.Any(x => organizationUnitIds.Contains(x.OrganizationUnitId))); } + if (id.HasValue) + { + return query.Where(x => x.Id == id); + } + return query .WhereIf( !filter.IsNullOrWhiteSpace(), @@ -487,7 +492,6 @@ public class EfCoreIdentityUserRepository : EfCoreRepository p.CreationTime <= maxCreationTime) .WhereIf(minCreationTime != null, p => p.CreationTime >= minCreationTime) .WhereIf(maxModifitionTime != null, p => p.LastModificationTime <= maxModifitionTime) - .WhereIf(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime) - .WhereIf(id.HasValue, x => x.Id == id); + .WhereIf(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime); } } diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs index 97c41ad3f5..e580bd846d 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs @@ -459,6 +459,10 @@ public class MongoIdentityUserRepository : MongoDbRepository identityUser.Roles.Any(x => x.RoleId == roleId.Value) || identityUser.OrganizationUnits.Any(x => organizationUnitIds.Contains(x.OrganizationUnitId))); } + if (id.HasValue) + { + return query.Where(x => x.Id == id); + } return query .WhereIf( @@ -484,7 +488,6 @@ public class MongoIdentityUserRepository : MongoDbRepository p.CreationTime <= maxCreationTime) .WhereIf(minCreationTime != null, p => p.CreationTime >= minCreationTime) .WhereIf(maxModifitionTime != null, p => p.LastModificationTime <= maxModifitionTime) - .WhereIf(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime) - .WhereIf(id.HasValue, x => x.Id == id); + .WhereIf(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime); } } \ No newline at end of file From 75d679a2db2a9f002111c634a6e9dd6aa56bf537 Mon Sep 17 00:00:00 2001 From: Mansur Besleney <84874279+MansurBesleney@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:44:16 +0300 Subject: [PATCH 06/10] Update EfCoreIdentityUserRepository.cs --- .../EfCoreIdentityUserRepository.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs index 002261df46..a623894625 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs @@ -456,6 +456,11 @@ public class EfCoreIdentityUserRepository : EfCoreRepository x.Id == id); + } if (roleId.HasValue) { @@ -464,11 +469,6 @@ public class EfCoreIdentityUserRepository : EfCoreRepository identityUser.Roles.Any(x => x.RoleId == roleId.Value) || identityUser.OrganizationUnits.Any(x => organizationUnitIds.Contains(x.OrganizationUnitId))); } - if (id.HasValue) - { - return query.Where(x => x.Id == id); - } - return query .WhereIf( !filter.IsNullOrWhiteSpace(), From c1d23fb7547b9bd5943e2e39416b9a5a4c9e9b91 Mon Sep 17 00:00:00 2001 From: Mansur Besleney <84874279+MansurBesleney@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:44:58 +0300 Subject: [PATCH 07/10] Update MongoIdentityUserRepository.cs --- .../Identity/MongoDB/MongoIdentityUserRepository.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs index e580bd846d..64c47f8d2c 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs @@ -449,6 +449,11 @@ public class MongoIdentityUserRepository : MongoDbRepository x.Id == id); + } + if (roleId.HasValue) { var organizationUnitIds = (await GetQueryableAsync(cancellationToken)) @@ -459,11 +464,6 @@ public class MongoIdentityUserRepository : MongoDbRepository identityUser.Roles.Any(x => x.RoleId == roleId.Value) || identityUser.OrganizationUnits.Any(x => organizationUnitIds.Contains(x.OrganizationUnitId))); } - if (id.HasValue) - { - return query.Where(x => x.Id == id); - } - return query .WhereIf( !filter.IsNullOrWhiteSpace(), @@ -490,4 +490,4 @@ public class MongoIdentityUserRepository : MongoDbRepository p.LastModificationTime <= maxModifitionTime) .WhereIf(minModifitionTime != null, p => p.LastModificationTime >= minModifitionTime); } -} \ No newline at end of file +} From 6a947809b0c8b01fda1c012d494b64c159afd0fd Mon Sep 17 00:00:00 2001 From: sumeyye Date: Tue, 28 Jan 2025 14:20:07 +0300 Subject: [PATCH 08/10] fix: enum type checks for the service proxy generation --- .../packages/schematics/src/utils/service.ts | 26 ++++++++++++++++--- .../packages/schematics/src/utils/type.ts | 10 ++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/npm/ng-packs/packages/schematics/src/utils/service.ts b/npm/ng-packs/packages/schematics/src/utils/service.ts index b32dd5fba5..8f48ed3693 100644 --- a/npm/ng-packs/packages/schematics/src/utils/service.ts +++ b/npm/ng-packs/packages/schematics/src/utils/service.ts @@ -18,6 +18,7 @@ import { createTypeAdapter, createTypeParser, createTypesToImportsReducer, + getTypeForEnumList, removeTypeModifiers, } from './type'; import { eBindingSourceId } from '../enums'; @@ -80,7 +81,19 @@ export function createActionToBodyMapper() { const adaptType = createTypeAdapter(); return ({ httpMethod, parameters, returnValue, url }: Action) => { - const responseType = adaptType(returnValue.typeSimple); + let responseType = adaptType(returnValue.typeSimple); + if (responseType.includes('enum')) { + const type = returnValue.typeSimple.replace('enum', returnValue.type); + + if (responseType === 'enum') { + responseType = adaptType(type); + } + + if (responseType === 'enum[]') { + const normalizedType = getTypeForEnumList(type); + responseType = adaptType(normalizedType); + } + } const responseTypeWithNamespace = returnValue.typeSimple; const body = new Body({ method: httpMethod, responseType, url, responseTypeWithNamespace }); @@ -109,7 +122,12 @@ export function createActionToSignatureMapper() { if (isFormData || isFormArray) { return new Property({ name: p.name, type: 'FormData' }); } - const type = adaptType(p.typeSimple); + + let type = adaptType(p.typeSimple); + if (p.typeSimple === 'enum' || p.typeSimple === '[enum]') { + type = adaptType(p.type); + } + const parameter = new Property({ name: p.name, type }); parameter.setDefault(p.defaultValue); parameter.setOptional(p.isOptional); @@ -183,7 +201,9 @@ function createActionToImportsReducer( parseGenerics(paramType) .toGenerics() .forEach(type => { - if (types[type]) acc.push({ type, isEnum: types[type].isEnum }); + if (types[type]) { + acc.push({ type, isEnum: types[type].isEnum }); + } }), ); diff --git a/npm/ng-packs/packages/schematics/src/utils/type.ts b/npm/ng-packs/packages/schematics/src/utils/type.ts index 63c69c7aec..f6d16b39df 100644 --- a/npm/ng-packs/packages/schematics/src/utils/type.ts +++ b/npm/ng-packs/packages/schematics/src/utils/type.ts @@ -58,6 +58,10 @@ export function removeTypeModifiers(type: string) { return type.replace(/\[\]/g, ''); } +export function getTypeForEnumList(type: string) { + return type.replace(/^.*<([^>]+)>.*$/, '[$1]'); +} + export function createTypesToImportsReducer(solution: string, namespace: string) { const mapTypeToImport = createTypeToImportMapper(solution, namespace); @@ -68,7 +72,7 @@ export function createTypesToImportsReducer(solution: string, namespace: string) return; } - if(newImport.specifiers.some(f => f.toLocaleLowerCase() === type.toLocaleLowerCase())){ + if (newImport.specifiers.some(f => f.toLocaleLowerCase() === type.toLocaleLowerCase())) { return; } @@ -76,7 +80,7 @@ export function createTypesToImportsReducer(solution: string, namespace: string) ({ keyword, path }) => keyword === newImport.keyword && path === newImport.path, ); - if (!existingImport){ + if (!existingImport) { return imports.push(newImport); } @@ -101,7 +105,7 @@ export function createTypeToImportMapper(solution: string, namespace: string) { const refs = [removeTypeModifiers(type)]; const specifiers = [adaptType(simplifyType(refs[0]).split('<')[0])]; let path = relativePathToModel(namespace, modelNamespace); - + if (VOLO_REGEX.test(type)) { path = '@abp/ng.core'; } From f8273f61c8eb6af1dc9d0c5d75d7094449de3675 Mon Sep 17 00:00:00 2001 From: EngincanV Date: Tue, 28 Jan 2025 14:21:46 +0300 Subject: [PATCH 09/10] Add missing menu items for theming docs --- docs/en/docs-nav.json | 8 ++++++++ docs/en/framework/ui/blazor/theming.md | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 3227cabdc1..8054c37dbe 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -1316,6 +1316,10 @@ "text": "LeptonX Lite", "path": "ui-themes/lepton-x-lite/blazor.md" }, + { + "text": "LeptonX", + "path": "ui-themes/lepton-x/blazor.md" + }, { "text": "Branding", "path": "framework/ui/blazor/branding.md" @@ -1635,6 +1639,10 @@ { "text": "LeptonX Lite", "path": "ui-themes/lepton-x-lite/angular.md" + }, + { + "text": "LeptonX", + "path": "ui-themes/lepton-x/angular.md" } ] }, diff --git a/docs/en/framework/ui/blazor/theming.md b/docs/en/framework/ui/blazor/theming.md index 0c7d6f8406..9a056580bb 100644 --- a/docs/en/framework/ui/blazor/theming.md +++ b/docs/en/framework/ui/blazor/theming.md @@ -27,7 +27,7 @@ Currently, three themes are **officially provided**: * The [Basic Theme](basic-theme.md) is the minimalist theme with the plain Bootstrap style. It is **open source and free**. * The [Lepton Theme](https://abp.io/themes) is a **commercial** theme developed by the core ABP team and is a part of the [ABP](https://abp.io/) license. -* The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has a [commercial](https://docs.abp.io/en/commercial/latest/themes/lepton-x/blazor) and a [lite](../../../ui-themes/lepton-x-lite/blazor.md) version. +* The [LeptonX Theme](https://x.leptontheme.com/) is a theme that has a [commercial](../../../ui-themes/lepton-x/blazor.md) and a [lite](../../../ui-themes/lepton-x-lite/blazor.md) version. ## Overall From 2e639a937e3cffce3843145f6955293e23fbc26d Mon Sep 17 00:00:00 2001 From: EngincanV Date: Tue, 28 Jan 2025 14:27:07 +0300 Subject: [PATCH 10/10] Update blazor/overall.md and mention provided Blazor hosting models. --- docs/en/framework/ui/blazor/overall.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/en/framework/ui/blazor/overall.md b/docs/en/framework/ui/blazor/overall.md index 12cef14552..54a545759e 100644 --- a/docs/en/framework/ui/blazor/overall.md +++ b/docs/en/framework/ui/blazor/overall.md @@ -1,14 +1,17 @@ # Blazor UI: Overall -## Introduction +[Blazor](https://docs.microsoft.com/en-us/aspnet/core/blazor/) is a framework for building interactive client-side web UI with .NET. It enables .NET developers to create Single-Page Web Applications using C# and the Razor syntax. -[Blazor](https://docs.microsoft.com/en-us/aspnet/core/blazor/) is a framework for building interactive client-side web UI with .NET. It is promising for a .NET developer that you can create Single-Page Web Applications using C# and the Razor syntax. +ABP provides comprehensive infrastructure and integrations that make your Blazor development easier, comfortable and enjoyable. ABP supports multiple Blazor hosting models: -ABP provides infrastructure and integrations that make your Blazor development even easier, comfortable and enjoyable. +* **Blazor WebAssembly (WASM)**: Client-side hosting model where the entire application runs in the browser using WebAssembly +* **Blazor Server**: Server-side hosting model with a real-time SignalR connection +* **Blazor WebApp**: The new hybrid/united model introduced in .NET 8 combining the benefits of Server and WebAssembly approaches +* **MAUI Blazor**: For building cross-platform native applications using Blazor & MAUI -This document provides an overview for the ABP Blazor UI integration and highlights some major features. +This document provides an overview of the ABP Blazor UI integration and highlights some major features. -### Getting Started +## Getting Started You can follow the documents below to start with the ABP and the Blazor UI now: @@ -94,7 +97,7 @@ These libraries are selected as the base libraries and available to the applicat > Bootstrap's JavaScript part is not used since the Blazorise library already provides the necessary functionalities to the Bootstrap components in a native way. -> Beginning from June, 2021, the Blazorise library has dual licenses; open source & commercial. Based on your yearly revenue, you may need to buy a commercial license. See [this post](https://blazorise.com/news/announcing-2022-blazorise-plans-and-pricing-updates) to learn more. The Blazorise license is bundled with ABP and commercial customers doesn’t need to buy an extra Blazorise license. +> Beginning from June, 2021, the Blazorise library has dual licenses; open source & commercial. Based on your yearly revenue, you may need to buy a commercial license. See [this post](https://blazorise.com/news/announcing-2022-blazorise-plans-and-pricing-updates) to learn more. The Blazorise license is bundled with ABP and commercial customers doesn't need to buy an extra Blazorise license. ### The Layout