• 基于ABP的AppUser对象扩展


    🚀 优质资源分享 🚀

    学习路线指引(点击解锁)知识定位人群定位
    🧡 Python实战微信订餐小程序 🧡进阶级本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
    💛Python量化交易实战💛入门级手把手带你打造一个易扩展、更安全、效率更高的量化交易系统

    在ABP中AppUser表的数据字段是有限的,现在有个场景是和小程序对接,需要在AppUser表中添加一个OpenId字段。今天有个小伙伴在群中遇到的问题是基于ABP的AppUser对象扩展后,用户查询是没有问题的,但是增加和更新就会报"XXX field is required"的问题。本文以AppUser表扩展OpenId字段为例进行介绍。

    一.AppUser实体表

    AppUser.cs位于BaseService.Domain项目中,如下:

    public class AppUser : FullAuditedAggregateRoot, IUser
    {
        public virtual Guid? TenantId { get; private set; }
        public virtual string UserName { get; private set; }
        public virtual string Name { get; private set; }
        public virtual string Surname { get; private set; }
        public virtual string Email { get; private set; }
        public virtual bool EmailConfirmed { get; private set; }
        public virtual string PhoneNumber { get; private set; }
        public virtual bool PhoneNumberConfirmed { get; private set; }
    
        // 微信应用唯一标识
        public string OpenId { get; set; }
    
        private AppUser()
        {
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    因为AppUser继承自聚合根,而聚合根默认都实现了IHasExtraProperties接口,否则如果想对实体进行扩展,那么需要实体实现IHasExtraProperties接口才行。

    二.实体扩展管理

    BaseEfCoreEntityExtensionMappings.cs位于BaseService.EntityFrameworkCore项目中,如下:

    public class BaseEfCoreEntityExtensionMappings
    {
        private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
    
        public static void Configure()
        {
            BaseServiceModuleExtensionConfigurator.Configure();
    
            OneTimeRunner.Run(() =>
            {
                ObjectExtensionManager.Instance
                    .MapEfCorePropertystring>(nameof(AppUser.OpenId), (entityBuilder, propertyBuilder) =>
     {
     propertyBuilder.HasMaxLength(128);
     propertyBuilder.HasDefaultValue("");
     propertyBuilder.IsRequired();
     }
     );
     });
     }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    三.数据库上下文

    BaseServiceDbContext.cs位于BaseService.EntityFrameworkCore项目中,如下:

    [ConnectionStringName("Default")]
    public class BaseServiceDbContext : AbpDbContext
    {
     ......
     
     public BaseServiceDbContext(DbContextOptions options): base(options)
     {
     }
    
     protected override void OnModelCreating(ModelBuilder builder)
     {
     base.OnModelCreating(builder);
    
     builder.Entity(b =>
     {
     // AbpUsers和IdentityUser共享相同的表
     b.ToTable(AbpIdentityDbProperties.DbTablePrefix + "Users"); 
     
     b.ConfigureByConvention();
     b.ConfigureAbpUser();
     
     b.Property(x => x.OpenId).HasMaxLength(128).HasDefaultValue("").IsRequired().HasColumnName(nameof(AppUser.OpenId));
     });
    
     builder.ConfigureBaseService();
     }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    四.数据库迁移和更新

    1.数据库迁移

    dotnet ef migrations add add\_appuser\_openid
    
    
    • 1
    • 2

    2.数据库更新

    dotnet ef database update
    
    
    • 1
    • 2

    3.对额外属性操作

    数据库迁移和更新后,在AbpUsers数据库中就会多出来一个OpenId字段,然后在后端中就可以通过SetProperty或者GetProperty来操作额外属性了:

    // 设置额外属性
    var user = await _identityUserRepository.GetAsync(userId);
    user.SetProperty("Title", "My custom title value!");
    await _identityUserRepository.UpdateAsync(user);
    
    // 获取额外属性
    var user = await _identityUserRepository.GetAsync(userId);
    return user.GetProperty("Title");
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    但是在前端呢,主要是通过ExtraProperties字段这个json类型来操作额外属性的。

    五.应用层增改操作

    UserAppService.cs位于BaseService.Application项目中,如下:

    1.增加操作

    [Authorize(IdentityPermissions.Users.Create)]
    public async Task Create(BaseIdentityUserCreateDto input)
    {
        var user = new IdentityUser(
            GuidGenerator.Create(),
            input.UserName,
            input.Email,
            CurrentTenant.Id
        );
    
        input.MapExtraPropertiesTo(user);
    
        (await UserManager.CreateAsync(user, input.Password)).CheckErrors();
        await UpdateUserByInput(user, input);
    
        var dto = ObjectMapper.Map(user);
    
     foreach (var id in input.JobIds)
     {
     await \_userJobsRepository.InsertAsync(new UserJob(CurrentTenant.Id, user.Id, id));
     }
    
     foreach (var id in input.OrganizationIds)
     {
     await \_userOrgsRepository.InsertAsync(new UserOrganization(CurrentTenant.Id, user.Id, id));
     }
    
     await CurrentUnitOfWork.SaveChangesAsync();
    
     return dto;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    2.更新操作

    [Authorize(IdentityPermissions.Users.Update)]
    public async Task UpdateAsync(Guid id, BaseIdentityUserUpdateDto input)
    {
        UserManager.UserValidators.Clear();
        
        var user = await UserManager.GetByIdAsync(id);
        user.ConcurrencyStamp = input.ConcurrencyStamp;
    
        (await UserManager.SetUserNameAsync(user, input.UserName)).CheckErrors();
    
        await UpdateUserByInput(user, input);
        input.MapExtraPropertiesTo(user);
    
        (await UserManager.UpdateAsync(user)).CheckErrors();
    
        if (!input.Password.IsNullOrEmpty())
        {
            (await UserManager.RemovePasswordAsync(user)).CheckErrors();
            (await UserManager.AddPasswordAsync(user, input.Password)).CheckErrors();
        }
    
        var dto = ObjectMapper.Map(user);
     dto.SetProperty("OpenId", input.ExtraProperties["OpenId"]);
     
     await \_userJobsRepository.DeleteAsync(\_ => \_.UserId == id);
    
     if (input.JobIds != null)
     {
     foreach (var jid in input.JobIds)
     {
     await \_userJobsRepository.InsertAsync(new UserJob(CurrentTenant.Id, id, jid));
     }
     }
    
     await \_userOrgsRepository.DeleteAsync(\_ => \_.UserId == id);
    
     if (input.OrganizationIds != null)
     {
     foreach (var oid in input.OrganizationIds)
     {
     await \_userOrgsRepository.InsertAsync(new UserOrganization(CurrentTenant.Id, id, oid));
     }
     }
    
     await CurrentUnitOfWork.SaveChangesAsync();
    
     return dto;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    3.UpdateUserByInput()函数

    上述增加和更新操作代码中用到的UpdateUserByInput()函数如下:

    protected virtual async Task UpdateUserByInput(IdentityUser user, IdentityUserCreateOrUpdateDtoBase input)
    {
        if (!string.Equals(user.Email, input.Email, StringComparison.InvariantCultureIgnoreCase))
        {
            (await UserManager.SetEmailAsync(user, input.Email)).CheckErrors();
        }
    
        if (!string.Equals(user.PhoneNumber, input.PhoneNumber, StringComparison.InvariantCultureIgnoreCase))
        {
            (await UserManager.SetPhoneNumberAsync(user, input.PhoneNumber)).CheckErrors();
        }
    
        (await UserManager.SetLockoutEnabledAsync(user, input.LockoutEnabled)).CheckErrors();
    
        user.Name = input.Name;
        user.Surname = input.Surname;
        
        user.SetProperty("OpenId", input.ExtraProperties["OpenId"]);
        
        if (input.RoleNames != null)
        {
            (await UserManager.SetRolesAsync(user, input.RoleNames)).CheckErrors();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    实体扩展的好处是不用继承实体,或者修改实体就可以对实体进行扩展,可以说是非常的灵活,但是实体扩展并不适用于复杂的场景,比如使用额外属性创建索引和外键、使用额外属性编写SQL或LINQ等。遇到这种情况该怎么办呢?有种方法是直接引用源码和添加字段。

    参考文献:
    [1]自定义应用模块:https://docs.abp.io/zh-Hans/abp/6.0/Customizing-Application-Modules-Guide
    [2]自定义应用模块-扩展实体:https://docs.abp.io/zh-Hans/abp/6.0/Customizing-Application-Modules-Extending-Entities
    [3]自定义应用模块-重写服务:https://docs.abp.io/zh-Hans/abp/6.0/Customizing-Application-Modules-Overriding-Services
    [4]ABP-MicroService:https://github.com/WilliamXu96/ABP-MicroService

  • 相关阅读:
    js 数组移除指定元素【函数封装】(含对象数组移除指定元素)
    torch模块常用方法总结
    Kafka服务端高性能技术细节原理:基于零拷贝的mmap和sendfile
    牛客刷题<32~34>非整数倍数和整数倍数数据位宽转换
    Quanergy欢迎Lori Sundberg出任首席人力资源官
    .NET通过源码深究依赖注入原理
    Kubernetes:(十七)Helm概述、安装及配置
    医学知识和医疗应用开发交叉领域中垂类大语言模型应用相关研究
    实现“羊了个羊”玩法的消消乐(react)
    JavaScript
  • 原文地址:https://blog.csdn.net/u012804784/article/details/126258552