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()
{
}
}
因为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(nameof(AppUser.OpenId), (entityBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(128);
propertyBuilder.HasDefaultValue("");
propertyBuilder.IsRequired();
}
);
});
}
}
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();
}
}
dotnet ef migrations add add_appuser_openid
dotnet ef database update
数据库迁移和更新后,在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");
但是在前端呢,主要是通过ExtraProperties字段这个json类型来操作额外属性的。
UserAppService.cs位于BaseService.Application项目中,如下:
[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;
}
[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;
}
上述增加和更新操作代码中用到的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等。遇到这种情况该怎么办呢?有种方法是直接引用源码和添加字段。