在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

基于ABP的AppUser对象扩展的更多相关文章

  1. ABP(现代ASP.NET样板开发框架)系列之16、ABP应用层——数据传输对象(DTOs)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之16.ABP应用层——数据传输对象(DTOs) ABP是“ASP.NET Boilerplate Project ...

  2. ABP应用层——数据传输对象(DTOs)

    ABP应用层——数据传输对象(DTOs) 基于DDD的现代ASP.NET开发框架--ABP系列之16.ABP应用层——数据传输对象(DTOs) ABP是“ASP.NET Boilerplate Pro ...

  3. 基于 abp vNext 和 .NET Core 开发博客项目 - 定时任务最佳实战(三)

    上一篇(https://www.cnblogs.com/meowv/p/12974439.html)完成了全网各大平台的热点新闻数据的抓取,本篇继续围绕抓取完成后的操作做一个提醒.当每次抓取完数据后, ...

  4. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(一)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  5. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(二)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  6. 循序渐进VUE+Element 前端应用开发(23)--- 基于ABP实现前后端的附件上传,图片或者附件展示管理

    在我们一般系统中,往往都会涉及到附件的处理,有时候附件是图片文件,有时候是Excel.Word等文件,一般也就是可以分为图片附件和其他附件了,图片附件可以进行裁剪管理.多个图片上传管理,及图片预览操作 ...

  7. 基于ABP落地领域驱动设计-01.全景图

    什么是领域驱动设计? 领域驱动设计(简称:DDD)是一种针对复杂需求的软件开发方法.将软件实现与不断发展的模型联系起来,专注于核心领域逻辑,而不是基础设施细节.DDD适用于复杂领域和大规模应用,而不是 ...

  8. 基于ABP落地领域驱动设计-02.聚合和聚合根的最佳实践和原则

    目录 前言 聚合 聚合和聚合根原则 包含业务原则 单个单元原则 事务边界原则 可序列化原则 聚合和聚合根最佳实践 只通过ID引用其他聚合 用于 EF Core 和 关系型数据库 保持聚合根足够小 聚合 ...

  9. 基于ABP落地领域驱动设计-03.仓储和规约最佳实践和原则

    目录 系列文章 仓储 仓储的通用原则 仓储中不包含领域逻辑 规约 在实体中使用规约 在仓储中使用规约 组合规约 学习帮助 围绕DDD和ABP Framework两个核心技术,后面还会陆续发布核心构件实 ...

随机推荐

  1. unity---世界坐标和本地坐标的转换

    会受到缩放的影响 用的比较多的是 本地坐标转换为世界坐标,比如在自己周围释放物体 当物体发生旋转,方向会发生变化

  2. drools中的条件 when

    目录 1.介绍 2.语法结构 3.模式例子 3.1 单个对象匹配 3.2 匹配任何对象 3.3 带条件匹配 3.3.1 注意事项 3.4 嵌套属性的匹配 3.4.1 访问单个嵌套属性 3.4.2 访问 ...

  3. TyepScript学习

    前提 JS缺陷 (1)变量频繁变换类型,类型不明确难以维护 TS定义 (1)定义 以JavaScript为基础构建的语音,一个JavaScript的超集,扩展js添加了类型, 可以在任何支持js的平台 ...

  4. st表 LCA

    我当时知道ST表可以 \(O(1)\) 求 LCA 的时候是极为震惊的,可以在需要反复使用 LCA 的时候卡常使用. ST表!用于解决 RMQ问题 ST表 我可能写得不好,看专业的 怎么实现? 考虑把 ...

  5. 《回炉重造 Java 基础》——集合(容器)

    整体框架 绿色代表接口/抽象类:蓝色代表类. 主要由两大接口组成,一个是「Collection」接口,另一个是「Map」接口. 前言 以前刚开始学习「集合」的时候,由于没有好好预习,也没有学好基础知识 ...

  6. ShardingSphere 异构迁移最佳实践:将3.5亿量级的顾客系统 RTO 减少60倍

    Apache ShardingSphere 助力当当 3.5 亿用户量级顾客系统重构,由 PHP+SQL Server 技术栈无缝转型为 Java+ShardingSphere+MySQL,性能.可用 ...

  7. 数字化转型之数字资产知识库(springboot+es+vue+neo4j)

    前言 在数字化高度普及的时代,企事业机关单位在日常工作中会产生大量的文档,例如医院制度汇编,企业知识共享库等.针对这些文档性的东西,手工纸质化去管理是非常消耗工作量的,并且纸质化查阅难,易损耗,所以电 ...

  8. Vscode个性化设置:让一个小萌妹陪你敲代码

    前言 大家平时都用什么代码编辑器啊!我个人比较喜欢用vscode,因为有以下几点: 开源,免费: 自定义配置 集成git 智能提示强大 支持各种文件格式(html/jade/css/less/sass ...

  9. Volcano成Spark默认batch调度器

    摘要:对于Spark用户而言,借助Volcano提供的批量调度.细粒度资源管理等功能,可以更便捷的从Hadoop迁移到Kubernetes,同时大幅提升大规模数据分析业务的性能. 2022年6月16日 ...

  10. 『现学现忘』Docker基础 — 39、实战:自定义Tomcat9镜像

    目录 1.目标 2.准备 3.编写Dockerfile文件 4.构建镜像 5.启动镜像 6.验证容器是否能够访问 7.向容器中部署WEB项目,同时验证数据卷挂载 (1)准备一个简单的WEB项目 (2) ...