在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. Hadoop安装学习(第三天)

    学习任务: 1.解压jdk和hadoop包 2.安装jdk 3.修改hadoop配置文件 4.hadoop格式化 5.hadoop启动 出现的问题:hadoop可以正常启动,但是端口9000丢失,导致 ...

  2. Fail2ban 配置详解 动作配置

    ### # 包含配置 ### [INCLUDES] before = iptables-common.conf ### # 定义动作 ### [Definition] actionstart = &l ...

  3. SPPNet(特征金字塔池化)学习笔记

    SPPNet paper:Spatial pyramid pooling in deep convolutional networks for visual recognition code 首先介绍 ...

  4. vsftp 详解

    1.默认配置: 1>允许匿名用户和本地用户登陆.     anonymous_enable=YES     local_enable=YES2>匿名用户使用的登陆名为ftp或anonymo ...

  5. JAVA - 如何判断一个类是无用的类

    JAVA - 如何判断一个类是无用的类 方法区主要回收的是无用的类,那么如何判断一个类是无用的类的呢? 判定一个常量是否是"废弃常量"比较简单,而要判定一个类是否是"无用 ...

  6. 腾讯云数据库TDSQL-大咖论道 | 基础软件的过去、现在、未来

    近十年来,中国基础软件发展势头迅猛,市场前景看高,越来越多的企业也正在进行基础软件升级.那中国基础软件行业目前在国际市场上有什么优势,面临哪些困境,以及未来基础软件行业会如何发展呢?腾讯云数据库邀请沙 ...

  7. DevOps落地实践点滴和踩坑记录-(1)

    记录初衷 本人一直在从事企业内DevOps落地实践的工作,走了不少弯路,也努力在想办法解决面临的问题,期间也经历过不少人和事情,最近突然有想法把经历过的,不管好的不好的都记录下来,分享给和我一样的一线 ...

  8. ExtJS 布局-Absolute布局(Absolute layout)

    更新记录: 2022年5月31日 发布本篇 1.说明 使用xy配置项设置子组件在父容器中绝对位置,本质是将子组件的CSS的position设置为absolute,然后使用x和y配置项映射到CSS的to ...

  9. .NET中如何在同步代码块中调用异步方法

    更新记录 本文迁移自Panda666原博客,原发布时间:2021年7月2日. 在同步代码块中调用异步方法,方法有很多. 一.对于有返回值的Task 在同步代码块中直接访问 Task 的 Result ...

  10. 倾斜摄影3D模型|手工建模|BIM模型 轻量化处理

    一.什么是大场景? 顾名思义,大场景就是能够从一个鸟瞰的角度看到一个大型场景的全貌,比如一个园区.一座城市.一个国家甚至是整个地球.但过去都以图片记录下大场景,如今我们可以通过建造3D模型来还原大场景 ...