在ABP中AppUser表的数据字段是无限的,当初有个场景是和小程序对接,须要在AppUser表中增加一个OpenId字段。明天有个小伙伴在群中遇到的问题是基于ABP的AppUser对象扩大后,用户查问是没有问题的,然而减少和更新就会报"XXX field is required"的问题。本文以AppUser表扩大OpenId字段为例进行介绍。

一.AppUser实体表

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

public class AppUser : FullAuditedAggregateRoot<Guid>, 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()    {    }}

因为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                .MapEfCoreProperty<IdentityUser, string>(nameof(AppUser.OpenId), (entityBuilder, propertyBuilder) =>                    {                        propertyBuilder.HasMaxLength(128);                        propertyBuilder.HasDefaultValue("");                        propertyBuilder.IsRequired();                    }                );        });    }}

三.数据库上下文

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

[ConnectionStringName("Default")]public class BaseServiceDbContext : AbpDbContext<BaseServiceDbContext>{    ......        public BaseServiceDbContext(DbContextOptions<BaseServiceDbContext> options): base(options)    {    }    protected override void OnModelCreating(ModelBuilder builder)    {        base.OnModelCreating(builder);        builder.Entity<AppUser>(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.数据库迁徙

dotnet ef migrations add add_appuser_openid

2.数据库更新

dotnet ef database update

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<string>("Title");

然而在前端呢,次要是通过ExtraProperties字段这个json类型来操作额定属性的。

五.应用层增改操作

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

1.减少操作

[Authorize(IdentityPermissions.Users.Create)]public async Task<IdentityUserDto> 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<IdentityUser, IdentityUserDto>(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;}

2.更新操作

[Authorize(IdentityPermissions.Users.Update)]public async Task<IdentityUserDto> 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<IdentityUser, IdentityUserDto>(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;}

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();    }}

  实体扩大的益处是不必继承实体,或者批改实体就能够对实体进行扩大,能够说是十分的灵便,然而实体扩大并不适用于简单的场景,比方应用额定属性创立索引和外键、应用额定属性编写SQL或LINQ等。遇到这种状况该怎么办呢?有种办法是间接援用源码和增加字段。

参考文献:
[1]自定义利用模块:https://docs.abp.io/zh-Hans/a...
[2]自定义利用模块-扩大实体:https://docs.abp.io/zh-Hans/a...
[3]自定义利用模块-重写服务:https://docs.abp.io/zh-Hans/a...
[4]ABP-MicroService:https://github.com/WilliamXu9...

本文由mdnice多平台公布