Open Closed

Customizing Application Modules Extending User Entity i.e. ApbUsers #1826


0
lalitChougule created
  • ABP Framework version: v4.3.1
  • UI type: Angular
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): no / yes
  • Exception message and stack trace: N.A
  • Steps to reproduce the issue:" N.A

I need to add one column to AbpUsers table which I can use while querying abpUsers table I followed this https://docs.abp.io/en/abp/4.4/Customizing-Application-Modules-Extending-Entities#entity-extensions-ef-core

I was able to do migration and update-database. Column was created in database but I was not able to find the column while querying.

var allUsers = await UserRepository.GetListAsync();
var myConditionalUsers = allUsers.Where(x => x.PropertyAdded == 1).ToList();

Not able to find PropertyAdded

Can you provide quick solution?


17 Answer(s)
  • 0
    liangshiwei created
    Support Team

    Hi,

    I think you need : https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.ef.property?view=efcore-5.0#examples

  • 0
    lalitChougule created

    Hi liangshiwei,

    I'm sorry but I was not able to implement this : https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.ef.property?view=efcore-5.0#examples in my project. Can you provide me some sample code how to implement it in abp.io project. It will be really appreciable

  • 0
    liangshiwei created
    Support Team

    HI,

    You can implement it in the entityframeworkcore project

    public class MyUserRepository : EfCoreRepository<IIdentityDbContext, IdentityUser, Guid> , IMyUserRepository
    {
        public MyUserRepository(IDbContextProvider<IIdentityDbContext> dbContextProvider) : base(dbContextProvider)
        {
        }
    
        public async Task<List<IdentityUser>> GetListAsync(.....)
        {
            return await (await GetDbSetAsync()).Where(x => EF.Property<int>(x, "PropertyAdded") == 1).ToListAsync();
        }
    }
    
  • 0
    lalitChougule created

    Hi liangshiwei,

    I can do this but the problem with this approach is I need to do changes in many files I have done alot of custom logic implementation in IdentityUserAppService and IdentityRoleAppServcice.

    This is abp's method IdentityUserAppService :-

    [Authorize(IdentityPermissions.Users.Default)]
    public virtual async Task<PagedResultDto<IdentityUserDto>> GetListAsync(GetIdentityUsersInput input)
    {
    	//What I want is as below
    	//Want to fetch all the users with StatusId == 1 (StatusId i.e. Column added to IdentityUser) -- I am stuck in this part
    	//Then apply paging logic 
    	var count = await UserRepository.GetCountAsync(input.Filter);
    	var list = await UserRepository.GetListAsync(input.Sorting, input.MaxResultCount,               input.SkipCount, input.Filter);
    
    	return new PagedResultDto<IdentityUserDto>(
    		count,
    		ObjectMapper.Map<List<IdentityUser>, List<IdentityUserDto>>(list)
    	);
    }
    

    Or else is there a way where I can override UserRepository.GetListAsync() where I can pass StatusId value and get all the user only with those StatusId.

    Similarly for roles I have two module for which I need to categorize my Roles

    | RoleName | Category | | --- | --- | | AnchorAdmin | CatA | | ProgramAdmin | CatB |

    And while fetching roles via. RoleRepository.GetListAsync() suppose I want roles only specific for CatA. How do I do this ?

    Basically what I need is how to get the added extra column via. RoleRepository.GetListAsync() or UserRepository.GetListAsync() on which I can apply where clause or something as per my requirement.

    Or else Is there a way where I can use IRepository<AbpUser,Guid> or IRepository<AbpUserRole,Guid> like this to directly query it from AppService or say from overridden MyIdentityUserAppService ? If yes please provide some sample code if possible.

    I hope you got my requirement now ?

  • 0
    lalitChougule created

    I have one more requirement but It depends on the above case to work. Post successfull implementation of above usecase , I need to check while logging in to the application If the user is having some StatusId I dont want to generate token or you can say I dont want that user to login to the application and give some custom message saying you are in this status you can login once administrator change your status etc. etc.

    How can I achieve this and where to do respective changes. Please do revert on this requirement.

  • 0
    liangshiwei created
    Support Team

    Hi

    And while fetching roles via. RoleRepository.GetListAsync() suppose I want roles only specific for CatA.

    Yes you can do this, there are two ways:

    1. Filter the memory list
    var users = (await _userRepository.GetListAsync()).Where(x => x.GetProperty<int>("PropertyAdded") == 1).ToList();
    
    1. Use EF Core API

    see : https://docs.abp.io/en/abp/latest/Entity-Framework-Core#access-to-the-ef-core-api

    var users = (await _userRepository.GetDbSetAsync()).Where(x => EF.Property<int>(x, "PropertyAdded") == 1).ToListAsync();
    

    How can I achieve this and where to do respective changes.

    You can use the IUserValidator

    See https://github.com/abpframework/abp/issues/3990

  • 0
    lalitChougule created
    1. Use EF Core API

    see : https://docs.abp.io/en/abp/latest/Entity-Framework-Core#access-to-the-ef-core-api

    var users = (await _userRepository.GetDbSetAsync()).Where(x => EF.Property<int>(x, "PropertyAdded") == 1).ToListAsync(); 
    

    Is there a way where I find one item of this list and want to update its "PropertyAdded" column. How do I do this ?

    You can use the IUserValidator

    See https://github.com/abpframework/abp/issues/3990

    Is there any abp.io document where implementation of UserValidator class is provided ? Can you provide me sample of this class ? And how can I find my custom column here ?

    In the below code there is one option called options.SignIn.RequireConfirmedEmail If I would able to find my added column here I think it will resolve my issue.

    IdentityServerModule : AbpModule
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
    	var hostingEnvironment = context.Services.GetHostingEnvironment();
        var configuration = context.Services.GetConfiguration();
    	
    	Configure<IdentityOptions>(options =>
    	{
    		options.User.AllowedUserNameCharacters = null;
            //options.SignIn.RequireConfirmedEmail <-- talking about this above
    	});
    }
    
  • 0
    liangshiwei created
    Support Team

    Is there a way where I find one item of this list and want to update its "PropertyAdded" column. How do I do this ?

    I think the document it clear, you can check it: https://docs.abp.io/en/abp/latest/Object-Extensions#setproperty

    Is there any abp.io document where implementation of UserValidator class is provided ? Can you provide me sample of this class ? And how can I find my custom column here ?

    Example:

    public class MyUserValidator<TUser> : IUserValidator<TUser> where TUser : Volo.Abp.Identity.IdentityUser
    {
        private readonly IIdentityUserRepository _userRepository;
    
        public MyUserValidator(IIdentityUserRepository userRepository)
        {
            _userRepository = userRepository;
        }
    
        public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user)
        {
            //....
        }
    }
    
    
    PreConfigure<IdentityBuilder>(builder =>
    {
        builder.AddUserValidator<MyUserValidator<Volo.Abp.Identity.IdentityUser>>();
    });
    

    In the below code there is one option called options.SignIn.RequireConfirmedEmail If I would able to find my added column here I think it will resolve my issue.

    No you can't

  • 0
    lalitChougule created

    Hi

    I tried using https://docs.abp.io/en/abp/latest/Object-Extensions#setproperty for "PropertyAdded" i.e. IsActive

    By using below code

    public async Task<bool> MakeUserInactive(string userId)
    {
            var user = await _identityUserManager.FindByIdAsync(userId);
            var context = await _userRepository.GetDbContextAsync();
            context.Entry<Volo.Abp.Identity.IdentityUser>(user).Property("IsActive").CurrentValue = false;
            await CurrentUnitOfWork.SaveChangesAsync();
            return true;
    }                        
    

    Getting error : The property 'IdentityUser.IsActive' could not be found. Ensure that the property exists and has been included in the model.

    And By using below code :

    public async Task<bool> MakeUserInactive(string userId)
    {
        var user = await _identityUserManager.FindByIdAsync(userId);
        user.SetProperty("IsActive", false);
        return true;
    } 
                    
    

    IsActive is set in ExtraProperties but the column value of IsActive remains true.

    #2 My Validator class :

    public class LitmusUserValidator<TUser> : IUserValidator<TUser> where TUser : Volo.Abp.Identity.IdentityUser
        {
            private readonly IIdentityUserRepository _userRepository;
    
            public LitmusUserValidator(IIdentityUserRepository userRepository)
            {
                _userRepository = userRepository;
            }
    
            public async Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user)
            {
                var errors = new List<IdentityError>();
    
                var users = (await _userRepository.GetDbSetAsync())
                                .Where(x => EF.Property<bool>(x, "IsActive") == false 
                                    && EF.Property<Guid>(x, "Id") == user.Id)
                                .ToListAsync();
                //Used list in above code because I am not able to access "IsActive" i.e. Custom property added on single object.
    
                if(users.Result.Count != 0)
                {
                    errors.Add(new IdentityError
                    {
                        Description = "User with InActive status cannot login"
                    });
                }
    
                return errors.Any()
                ? IdentityResult.Failed(errors.ToArray())
                : IdentityResult.Success;
            }
        }
    

    LitmusIdentityServerModule : My debugger didn't even hit this LitmusUserValidator ValidateAsync method while logging in I was able to login with user having "IsActive" == false;

    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        PreConfigure<IdentityBuilder>(options => { 
                    options.AddUserValidator<LitmusUserValidator<Volo.Abp.Identity.IdentityUser>>();
                });
    }
    
  • 0
    liangshiwei created
    Support Team

    Getting error : The property 'IdentityUser.IsActive' could not be found. Ensure that the property exists and has been included in the model.

    See https://docs.abp.io/en/abp/latest/Entity-Framework-Core#mapefcoreproperty

    My debugger didn't even hit this LitmusUserValidator ValidateAsync method while logging in

    Try Put to PreConfigureServices

  • 0
    lalitChougule created

    Getting error : The property 'IdentityUser.IsActive' could not be found. Ensure that the property exists and has been included in the model.

    See https://docs.abp.io/en/abp/latest/Entity-Framework-Core#mapefcoreproperty

    Steps Which I followed

    #1 EntityFrameworkCore project : LitmusEfCoreEntityExtensionMappings

    public static class LitmusEfCoreEntityExtensionMappings
    {
    	private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
    
    	public static void Configure()
    	{
    		OneTimeRunner.Run(() =>
    		{
    			ObjectExtensionManager.Instance
    				.MapEfCoreProperty<IdentityUser, bool>(
    					"IsActive",
    					(entityBuilder, propertyBuilder) =>
    					{
    						propertyBuilder.HasDefaultValue(true);
    					}
    				);
    
    			ObjectExtensionManager.Instance
    				.MapEfCoreProperty<IdentityRole, RoleType>("RoleType");
    		});
    	}
    }
    

    I took reference of the same URL.

    My debugger didn't even hit this LitmusUserValidator ValidateAsync method while logging in

    Try Put to PreConfigureServices

    Not working doing this as well.

    May be we can connect and you can check personally.

  • 0
    liangshiwei created
    Support Team

    Okay,

    Please email me, [email protected]

  • 0
    liangshiwei created
    Support Team

    Try

    [ExposeServices(typeof(LoginModel))]
    public class MyIdentityServerSupportedLoginModel : IdentityServerSupportedLoginModel
    {
    
        public MyIdentityServerSupportedLoginModel(IAuthenticationSchemeProvider schemeProvider, IOptions<AbpAccountOptions> accountOptions, IIdentityServerInteractionService interaction, IClientStore clientStore, IEventService identityServerEvents, IOptions<IdentityOptions> identityOptions) : base(schemeProvider, accountOptions, interaction, clientStore, identityServerEvents, identityOptions)
        {
        }
    
        public override Task<IActionResult> OnPostAsync(string action)
        {
            // check....
            return base.OnPostAsync(action);
        }
    }
    
    var users = (await _roleRepository.GetDbSetAsync()).Where(x => EF.Property<int>(x, "PropertyAdded") == 1).ToListAsync();
    
  • 0
    lalitChougule created

    Hi liangshiwei,

    Try

    I tried this but this is not working, Can we connect once ?

    var users = (await _roleRepository.GetDbSetAsync()).Where(x => EF.Property<int>(x, "PropertyAdded") == 1).ToListAsync();

    Below code is working for user. In same way I want to do it for role. But there is no class like AppRoles, help me with this as well

    public async Task<bool> MakeUserInactiveOnDormacyDaysCompletionAsync(Guid userId)
    {
        try
        {
            var user = await _appUserRepository.GetAsync(userId);
            if (user != null)
            {
                user.Status = AbpUserStatusEnum.InActive;
                await _appUserRepository.UpdateAsync(user);
            }
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            return false;
        }
    }
    
  • 0
    liangshiwei created
    Support Team

    Hi,

    I make an example for you: https://1drv.ms/u/s!ArILR0PuqXwbrY52JSWR3-uvkIoKcw?e=N8vn8h

  • 0
    lalitChougule created

    Hi,

    Thanks for the example. I achieved my Login restriction requirement by Overriding SignInManager.CanSignInAsync();

    But I am still stuck in the below issue Can you reply on this for roles : How I did for user can I do it for roles ? check below code I am not able to access individual record and update it added property. Once this is done we can close this ticket

    public async Task<bool> MakeUserInactiveOnDormacyDaysCompletionAsync(Guid userId)
    {
        try
        {
            var user = await _appUserRepository.GetAsync(userId);
            if (user != null)
            {
                user.Status = AbpUserStatusEnum.InActive;
                await _appUserRepository.UpdateAsync(user);
            }
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            return false;
        }
    }
    
  • 0
    liangshiwei created
    Support Team

    Hi,

    I think the example will help you :) https://1drv.ms/u/s!ArILR0PuqXwbrY52JSWR3-uvkIoKcw?e=N8vn8h