I am currently using ABP 8.3.1 Pro version. There is a bug when trying to use tokens created via a custom grant. The main issue from the logs says:
[16:45:33 DBG] AuthenticationScheme: OpenIddict.Validation.AspNetCore was successfully authenticated. [16:45:33 DBG] Get dynamic claims cache for user: a9a8d44f-c8e3-482f-a870-66e93186d540 [16:45:33 DBG] Filling dynamic claims cache for user: a9a8d44f-c8e3-482f-a870-66e93186d540 [16:45:33 WRN] SessionId() claim not found for user: a9a8d44f-c8e3-482f-a870-66e93186d540, log out. [16:45:33 DBG] Remove dynamic claims cache for user: a9a8d44f-c8e3-482f-a870-66e93186d540 [16:45:33 WRN] The token is no longer valid because the user's session expired. [16:45:33 INF] Authorization failed. These requirements were not met: DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
I am removing the dynamic claim right now with this as a temporary solution.
context.Services.Configure<AbpClaimsPrincipalFactoryOptions>(options =>
{
options.IsDynamicClaimsEnabled = false;
});
But I would prefer to keep them for other flows of my application.
I would prefer that my custom grant is not part of dynamic claims (or it could if you provide some code to create sessions appropriately?) This page mentions that there is a Principal Contributor: https://abp.io/docs/latest/modules/identity/session-management#how-it-works So I tried to get rid of it with this command: options.RemoveEventHandler(OpenIddictValidateIdentitySessionValidationHandler.Descriptor); But I still get the error. Maybe there is a flag I can set on the context to avoid getting my principal attached with a sessionId, but I can’t really debug further because all these packages are in the Pro and not compiled with ILDASM.
Can you please provide the following:
Supporting code
public class MagicTokenHandler
{
public const string ExtensionGrantName = "magic_token";
public const string MagicTokenKey = "magic_token";
}
// https://github.com/abpframework/abp/blob/b2878b4d3dca82811a5fc1739dee29cc88669eaa/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/TokenController.cs#L49
// https://github.com/abpframework/abp/blob/b2878b4d3dca82811a5fc1739dee29cc88669eaa/docs/en/Community-Articles/2022-11-14-How-to-add-a-custom-grant-type-in-OpenIddict/POST.md
public class MagicTokenExtensionGrant(
IdentityUserManager identityUserManager,
SignInManager<Volo.Abp.Identity.IdentityUser> signInManager,
IUserClaimsPrincipalFactory<Volo.Abp.Identity.IdentityUser> userClaimsPrincipalFactory,
AbpOpenIddictClaimsPrincipalManager abpOpenIddictClaimsPrincipalManager,
IOpenIddictScopeManager openIddictScopeManager
) : ITokenExtensionGrant, IScopedDependency
{
public readonly string TOKEN_HANDLER = LinkUserTokenProviderConsts.LinkUserTokenProviderName;
public readonly string TOKEN_PURPOSE = LinkUserTokenProviderConsts.LinkUserLoginTokenPurpose;
public string Name => MagicTokenHandler.ExtensionGrantName;
public async Task<IActionResult> HandleAsync(ExtensionGrantContext context)
{
var magicToken = context.Request.GetParameter(MagicTokenHandler.MagicTokenKey).ToString();
if (string.IsNullOrEmpty(magicToken) || string.IsNullOrEmpty(context.Request.Username))
{
return GenericError;
}
var user = await identityUserManager.FindByNameAsync(context.Request.Username);
if (user == null)
{
return GenericError;
}
var result = await identityUserManager.VerifyUserTokenAsync(user, TOKEN_HANDLER, TOKEN_PURPOSE, magicToken);
if (!result)
{
return GenericError;
}
var principal = await signInManager.CreateUserPrincipalAsync(user);
var claimsPrincipal = await userClaimsPrincipalFactory.CreateAsync(user);
claimsPrincipal.SetScopes(principal.GetScopes());
claimsPrincipal.SetAudiences("VarScanner");
claimsPrincipal.SetResources(await GetResourcesAsync(context, principal.GetScopes()));
//For abp version >= 7.3
await abpOpenIddictClaimsPrincipalManager.HandleAsync(context.Request, principal);
return new Microsoft.AspNetCore.Mvc.SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, claimsPrincipal);
}
private async Task<IEnumerable<string>> GetResourcesAsync(ExtensionGrantContext context, ImmutableArray<string> scopes)
{
var resources = new List<string>();
if (!scopes.Any())
{
return resources;
}
await foreach (var resource in openIddictScopeManager.ListResourcesAsync(scopes))
{
resources.Add(resource);
}
return resources;
}
private ForbidResult GenericError => new ForbidResult(
new[] { OpenIddictServerAspNetCoreDefaults.AuthenticationScheme },
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidRequest
}!));
}
Got a random error suddenly, it cant load AbpConfigurationScript on the js side.
03:20:03 [INF] Executed action Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationScriptController.Get (Volo.Abp.AspNetCore.Mvc) in 70.155ms
03:20:03 [INF] Executed endpoint 'Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationScriptController.Get (Volo.Abp.AspNetCore.Mvc)'
03:20:04 [ERR] An unhandled exception has occurred while executing the request.
Volo.Abp.AbpException: Undefined feature: AuditLogging.SettingManagement
at Volo.Abp.Features.FeatureDefinitionManager.GetAsync(String name)
at Volo.Abp.Features.FeatureChecker.GetOrNullAsync(String name)
at Volo.Abp.Features.FeatureCheckerBase.IsEnabledAsync(String name)
at Volo.Abp.Features.FeatureCheckerExtensions.IsEnabledAsync(IFeatureChecker featureChecker, Boolean requiresAll, String[] featureNames)
at Volo.Abp.Features.RequireFeaturesSimpleStateChecker`1.IsEnabledAsync(SimpleStateCheckerContext`1 context)
at Volo.Abp.SimpleStateChecking.SimpleStateCheckerManager`1.InternalIsEnabledAsync(TState state, Boolean useBatchChecker)
at Volo.Abp.SimpleStateChecking.SimpleStateCheckerManager`1.IsEnabledAsync(TState state)
at Volo.Abp.Authorization.Permissions.PermissionChecker.IsGrantedAsync(ClaimsPrincipal claimsPrincipal, String[] names)
at Volo.Abp.Authorization.Permissions.PermissionChecker.IsGrantedAsync(String[] names)
at Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationAppService.GetAuthConfigAsync()
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
at Volo.Abp.GlobalFeatures.GlobalFeatureInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
at Volo.Abp.Auditing.AuditingInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationAppService.GetAsync(ApplicationConfigurationRequestOptions options)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
at Volo.Abp.GlobalFeatures.GlobalFeatureInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
at Volo.Abp.Auditing.AuditingInterceptor.ProceedByLoggingAsync(IAbpMethodInvocation invocation, AbpAuditingOptions options, IAuditingHelper auditingHelper, IAuditLogScope auditLogScope)
at Volo.Abp.Auditing.AuditingInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
at Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.AbpApplicationConfigurationScriptController.Get()
at lambda_method4685(Closure, Object)
Trying to add a new field on an entity, depending on project I manually uninstall abp suite version and use/install the abp suite that is based on the project's version in this case 7.1.0
However when trying to add the field, I'm getting the custom code tags but its broken, but even that was added on v8, not sure why its coming now on suite v7.
Only recent change was that I updated to latest yesterday to try out a test migration of the project from 7.1 to 8.2, but I've reverted.
Always used latest versions of CLI, so that version has never been a problem.
Getting an error for Concurrency Exception from AbpOpenIddictTokenStore, not sure what is happening, probably due to concurrency stamp, help is appreciated. It's happening after an OAUTH Login callback.
NOTE: running on load balanced environment.
Jul 18 01:47:06 ip-172-31-10-186 web: SELECT `o`.`Id`, `o`.`ApplicationId`, `o`.`AuthorizationId`, `o`.`ConcurrencyStamp`, `o`.`CreationDate`, `o`.`CreationTime`, `o`.`CreatorId`, `o`.`DeleterId`, `o`.`DeletionTime`, `o`.`ExpirationDate`, `o`.`ExtraProperties`, `o`.`IsDeleted`, `o`.`LastModificationTime`, `o`.`LastModifierId`, `o`.`Payload`, `o`.`Properties`, `o`.`RedemptionDate`, `o`.`ReferenceId`, `o`.`Status`, `o`.`Subject`, `o`.`Type`
Jul 18 01:47:06 ip-172-31-10-186 web: FROM `OpenIddictTokens` AS `o`
Jul 18 01:47:06 ip-172-31-10-186 web: WHERE (@__ef_filter__p_0 OR NOT (`o`.`IsDeleted`)) AND (`o`.`Id` = @__id_0)
Jul 18 01:47:06 ip-172-31-10-186 web: ORDER BY `o`.`Id`
Jul 18 01:47:06 ip-172-31-10-186 web: LIMIT 1
Jul 18 01:47:06 ip-172-31-10-186 web: 01:47:06 [INF] Executed DbCommand (16ms) [Parameters=[@p5='?' (DbType = Guid), @p0='?' (Size = 40), @p6='?' (Size = 40), @p1='?' (DbType = DateTime), @p2='?' (DbType = Guid), @p3='?' (Size = 4000), @p4='?' (Size = 100)], CommandType='Text', CommandTimeout='30']
Jul 18 01:47:06 ip-172-31-10-186 web: SET AUTOCOMMIT = 1;
Jul 18 01:47:06 ip-172-31-10-186 web: UPDATE `OpenIddictTokens` SET `ConcurrencyStamp` = @p0, `LastModificationTime` = @p1, `LastModifierId` = @p2, `Payload` = @p3, `ReferenceId` = @p4
Jul 18 01:47:06 ip-172-31-10-186 web: WHERE `Id` = @p5 AND `ConcurrencyStamp` = @p6;
Jul 18 01:47:06 ip-172-31-10-186 web: SELECT ROW_COUNT();
Jul 18 01:47:07 ip-172-31-10-186 web: 01:47:07 [WRN] There is an entry which is not saved due to concurrency exception:
Jul 18 01:47:07 ip-172-31-10-186 web: OpenIddictToken {Id: 3a13d5b2-5f8d-043f-eee2-1bf911ed7bac} Modified FK {ApplicationId: 3a13d507-855b-a008-1d8f-b2b5b6f05b1a} FK {AuthorizationId: 3a13d526-0d0d-ef2d-7bd5-21a0e3c43eda}
Jul 18 01:47:07 ip-172-31-10-186 web: 01:47:07 [ERR] The database operation was expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or deleted since entities were loaded. See https://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
Jul 18 01:47:07 ip-172-31-10-186 web: Volo.Abp.Data.AbpDbConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or deleted since entities were loaded. See https://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
Jul 18 01:47:07 ip-172-31-10-186 web: ---> Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or deleted since entities were loaded. See https://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
Jul 18 01:47:07 ip-172-31-10-186 web: at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ThrowAggregateUpdateConcurrencyExceptionAsync(RelationalDataReader reader, Int32 commandIndex, Int32 expectedRowsAffected, Int32 rowsAffected, CancellationToken cancellationToken)
Jul 18 01:47:07 ip-172-31-10-186 web: at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeResultSetWithRowsAffectedOnlyAsync(Int32 commandIndex, RelationalDataReader reader, CancellationToken cancellationToken)
Jul 18 01:47:07 ip-172-31-10-186 web: at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeAsync(RelationalDataReader reader, CancellationToken cancellationToken)
Jul 18 01:47:07 ip-172-31-10-186 web: at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
Jul 18 01:47:07 ip-172-31-10-186 web: at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
Jul 18 01:47:07 ip-172-31-10-186 web: at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
Jul 18 01:47:07 ip-172-31-10-186 web: at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
Jul 18 01:47:07 ip-172-31-10-186 web: at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
Jul 18 01:47:07 ip-172-31-10-186 web: at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
Jul 18 01:47:07 ip-172-31-10-186 web: at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
Jul 18 01:47:07 ip-172-31-10-186 web: at Pomelo.EntityFrameworkCore.MySql.Storage.Internal.MySqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
Jul 18 01:47:07 ip-172-31-10-186 web: at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
Jul 18 01:47:07 ip-172-31-10-186 web: at Volo.Abp.EntityFrameworkCore.AbpDbContext`1.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
Jul 18 01:47:07 ip-172-31-10-186 web: --- End of inner exception stack trace ---
Jul 18 01:47:07 ip-172-31-10-186 web: at Volo.Abp.EntityFrameworkCore.AbpDbContext`1.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
Jul 18 01:47:07 ip-172-31-10-186 web: at Volo.Abp.Domain.Repositories.EntityFrameworkCore.EfCoreRepository`2.UpdateAsync(TEntity entity, Boolean autoSave, CancellationToken cancellationToken)
Jul 18 01:47:07 ip-172-31-10-186 web: at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
Jul 18 01:47:07 ip-172-31-10-186 web: at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
Jul 18 01:47:07 ip-172-31-10-186 web: at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
Jul 18 01:47:07 ip-172-31-10-186 web: at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
Jul 18 01:47:07 ip-172-31-10-186 web: at Volo.Abp.OpenIddict.Tokens.AbpOpenIddictTokenStore.UpdateAsync(OpenIddictTokenModel token, CancellationToken cancellationToken)
Jul 18 01:47:07 ip-172-31-10-186 web: 01:47:07 [INF] Executed action Volo.Abp.OpenIddict.Controllers.AuthorizeController.HandleAsync (Volo.Abp.OpenIddict.AspNetCore) in 4617.1376ms
Jul 18 01:47:07 ip-172-31-10-186 web: 01:47:07 [INF] Executed endpoint 'Volo.Abp.OpenIddict.Controllers.AuthorizeController.HandleAsync (Volo.Abp.OpenIddict.AspNetCore)'
The docs say that
ABP creates the bundle as lazy from the provided files when it's first requested. For the subsequent calls, it's returned from the cache. That means if you conditionally add the files to the bundle, it's executed only once and any changes of the condition will not effect the bundle for the next requests.
Can this be automatically done during start, instead of the first request? I have a pretty complex UI with rive animations and such, and what is happening is when new instances spin up in a load balanced environment, and requests go to those, it takes a while for it to stabilize, and returns broken pages before its fully stabilized. It could even be some other issue. Help is appreciated.
Hello we've been using load balanced ABP MVC on AWS with a RDS MySQL/Aurora instances for a while now, and everything has been great. However for a newer application we have to use RDS proxy, but using the RDS proxy endpoint we are getting timeout issues.
From the same EC2 instance that hosts the api I've used mysql cli to connect to the proxy endpoint, and everything works smoothly, so there is no network / security group issues. I've tried a brand new ABP template that has no Redis Cache or DistributedLock provider, and its the same issue.
Any help would be appreciated
Currently trying to describe enum models in Swagger, don't want to change the API parameters, they should still use and return integers, but the description should show which integer means what.
I've tried this
options.SchemaFilter<EnumSchemaFilter>();
public class EnumSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (context.Type.IsEnum)
{
var array = new OpenApiArray();
array.AddRange(Enum.GetNames(context.Type).Select(n => new OpenApiString(n)));
// NSwag
schema.Extensions.Add("x-enumNames", array);
// Openapi-generator
schema.Extensions.Add("x-enum-varnames", array);
}
}
}
Also tried using the package Unchase.Swashbuckle.AspNetCore.Extensions
options.AddEnumsWithValuesFixFilters();
But results are the same, no change
Enum models are still
ABP Suite 8.0.4
I choose my entity name and all my options, then move to Properties tab, but I lose all my changes in Entity Info tab. If I click Save on the properties tab, it doesn't work, because all my changes in Entity info are gone, and vice versa, my changes in Properties tab are lost when i go to Entity info tab.
Edit: It seems page / content seems to refresh on tab change
I extended the existing IdentityUser with the extension system, and also had it mapped to a different column using ef core mapping, which is working fine for the UI side as well
ObjectExtensionManager.Instance.Modules()
.ConfigureIdentity(identity =>
{
identity.ConfigureUser(user =>
{
user.AddOrUpdateProperty<Guid?>(
"ClassroomId",
property =>
{
//property.Attributes.Add(new RequiredAttribute());
//validation rules
property.DisplayName = new FixedLocalizableString("Classroom");
property.UI.Lookup.Url = "/api/app/classrooms";
property.UI.Lookup.DisplayPropertyName = "name";
property.UI.Lookup.FilterParamName = "filterText";
}
);
});
});
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, Guid?>(
"ClassroomId",
(entityBuilder, propertyBuilder) =>
{
entityBuilder
.HasOne(typeof(Classroom))
.WithMany()
.HasForeignKey("ClassroomId");
}
);
How to query on that property when querying from database directly and not using client side evaluation?
The doc mentions 'Another approach can be creating your own entity mapped to the same database table (or collection for a MongoDB database).`
Tried by making a class AppUser that inherits from IdentityUser mapped to the same table 'AbpUsers' however that makes a discriminator column, and queries with the new class dont bring out all records, which I think is expected from EF Core TablePerHierarchy.
What's the ideal solution for querying something like this?
var users = await Users.Where(x=> x.GetClassroomId() == classId).ToListAsync();
public static class IdentityUserExtensions
{
private const string ClassroomIdPropertyName = "ClassroomId";
public static void SetClassroomId(this IdentityUser user, Guid? classroomId)
{
user.SetProperty(ClassroomIdPropertyName, classroomId);
}
public static Guid? GetClassroomId(this IdentityUser user)
{
return user.GetProperty<Guid?>(ClassroomIdPropertyName);
}
}
Also I would rather not go for a new New Entity with Its Own Database Table, and using local bus to keep them synchronized, unless its the last option
ABP Suite CRUD pages to create boilerplate is pretty good at the start of a project with the started entities.
However for example if there's custom logic in any of those rendered code, it will get overwritten, if for example a new field is added into an entity, and the code is regenerated. Is there a good way to handle this situation, or is auto CRUD functionality only for the very beginning?