当前位置:网站首页>基于ABP的AppUser对象扩展
基于ABP的AppUser对象扩展
2022-08-09 23:32:00 【阿升】
在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/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
边栏推荐
猜你喜欢
WPF DataGrid 使用数据模板
Golden Warehouse Database KingbaseGIS User Manual (6.4. Geometry Object Access Function)
基于 LSTM 的分布式能源发电预测(Matlab代码实现)
【集训DAY5】选数字【数学】
Jpa 查询view or 无主键的table
【集训DAY5】堆箱子【数学】
【猜凶手,猜名次,杨辉三角】经典小学奥数的代码逻辑是什么?
Wireshark classic practice and interview 13-point summary
上交所实时行情文件汇总
YOLOV5学习笔记(七)——训练自己数据集
随机推荐
Linux安装Oracle和postgrepSQL数据库
Spark基础【RDD单Value类型转换算子】
信息系统项目管理师核心考点(六十四)信息安全基础知识重要概念
WPF DataGrid using data templates
Jpa 查询view or 无主键的table
【SSL集训DAY2】Sort【树状数组】
深度剖析 Apache EventMesh 云原生分布式事件驱动架构
安全知识培训——消防安全
Distributed database problem (3): data consistency
RebatMq消息中间件(一) 各个中间件介绍
ES6 Beginner to Mastery #13: Extension Methods for Arrays 2
阿雷的血压有些低
【C语言】通讯录《静态内存版本》
AppUser object extension based on ABP
经济衰退即将来临前CIO控制成本的七种方法
聚焦热点 | ISC 2022软件供应链安全治理与运营论坛圆满落幕
Description of AirFlow
考柏的感慨
JVM Memory and Garbage Collection - 10. Direct Memory
大龄测试员刚迈过了 35 岁这个“坎儿”,和大家说点儿心里话