如何使用函数`MapExtraPropertiesTo`?
How to use the function `MapExtraPropertiesTo`?
ABP框架版本:4.4.3
PostgreSQL: 13.4
异常
代码:
public async Task<IdentityUserDto> CreateAsync(IdentityUserCreateDto input)
{
await IdentityOptions.SetAsync();
_logger.Debug("创建用户-input\r\n" + JsonConvert.SerializeObject(input, Formatting.Indented));
var user = new Volo.Abp.Identity.IdentityUser(
GuidGenerator.Create(),
input.UserName,
input.Email,
CurrentTenant.Id
);
input.MapExtraPropertiesTo(user);
(await UserManager.CreateAsync(user, input.Password)).CheckErrors();
await UpdateUserByInput(user, input);
(await UserManager.UpdateAsync(user)).CheckErrors();
_logger.Debug("创建用户-user\r\n" + JsonConvert.SerializeObject(user, Formatting.Indented));
await CurrentUnitOfWork.SaveChangesAsync();
return ObjectMapper.Map<Volo.Abp.Identity.IdentityUser, IdentityUserDto>(user);
}
我用MapExtraPropertiesTo
把IdentityUserCreateDto
变成了IdentityUser
。但是 IdentityUser
的 属性 ExtraProperties
丢失了。
日志:
2021-11-22 08:57:13.008 +08:00 [DBG] 创建用户-input
{
"Password": "xxxxxxxx",
"UserName": "05150123",
"Name": "xxx",
"Surname": "xxx",
"Email": "05150123@56kad.com",
"PhoneNumber": null,
"LockoutEnabled": true,
"RoleNames": null,
"ExtraProperties": {
"Sex": 0,
"UserType": 2,
"SchoolUserType": 0,
"SchoolCode": "05150123",
"Country": "中国",
"ProvinceId": "320000000000",
"CityId": "320900000000",
"DistrictId": null
}
}
2021-11-22 08:57:13.303 +08:00 [DBG] 创建用户-user
{
"TenantId": null,
"UserName": "05150123",
"NormalizedUserName": "05150123",
"Name": "xxx",
"Surname": "xxx",
"Email": "05150123@56kad.com",
"NormalizedEmail": "05150123@56KAD.COM",
"EmailConfirmed": false,
"PasswordHash": "AQAAAAEAACcQAAAAEBdhpA4Y3azFxR0f2LCfE0W3vL5PvOBCzzxBTxsP63KGUnCSpdiHnAcL5diniThA==",
"SecurityStamp": "PU324ZRHDW7OO2SZHZUZ3Y6HF2H26U4P",
"IsExternal": false,
"PhoneNumber": null,
"PhoneNumberConfirmed": false,
"TwoFactorEnabled": false,
"LockoutEnd": null,
"LockoutEnabled": true,
"AccessFailedCount": 0,
"Roles": [],
"Claims": [],
"Logins": [],
"Tokens": [],
"OrganizationUnits": [],
"IsDeleted": false,
"DeleterId": null,
"DeletionTime": null,
"LastModificationTime": "2021-11-22T08:57:13.2831132+08:00",
"LastModifierId": "3a004944-b21f-0bbc-2376-44007c2a9295",
"CreationTime": "2021-11-22T08:57:13.1768976+08:00",
"CreatorId": "3a004944-b21f-0bbc-2376-44007c2a9295",
"ExtraProperties": {}, //lose
"ConcurrencyStamp": "08c5abb6ab55424899f40d30b275975b",
"Id": "3a005752-7630-f90f-3d3d-38da149eb416"
}
IdentityExtensionsEntityFrameworkCoreModule.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity;
namespace IdentityExtensions.EntityFrameworkCore
{
[DependsOn(
typeof(IdentityExtensionsDomainModule),
typeof(AbpEntityFrameworkCoreModule)
)]
public class IdentityExtensionsEntityFrameworkCoreModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
IdentityExtensionsEfCoreEntityExtensionMappings.Configure();
base.PreConfigureServices(context);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<AppUserDbContext>(options =>
{
/* Remove "includeAllEntities: true" to create
* default repositories only for aggregate roots */
options.AddDefaultRepositories();
});
context.Services.AddAbpDbContext<AggregateUniqueUserDbContext>(options =>
{
/* Remove "includeAllEntities: true" to create
* default repositories only for aggregate roots */
options.AddDefaultRepositories();
});
context.Services.Configure<AbpDbContextOptions>(options =>
{
options.UseNpgsql(b => b.UseNetTopologySuite());
});
}
}
}
IdentityExtensionsEfCoreEntityExtensionMappings.cs
using Microsoft.EntityFrameworkCore;
using System;
using Volo.Abp.Identity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Threading;
namespace IdentityExtensions.EntityFrameworkCore
{
public static class IdentityExtensionsEfCoreEntityExtensionMappings
{
private static readonly OneTimeRunner OneTimeRunner = new();
public static void Configure()
{
OneTimeRunner.Run(() =>
{
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, Guid?>(
nameof(AppUser.UniqueId),
(typeBuilder, propertyBuilder) => { }
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.OuterId),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(AppUserConsts.MaxOuterIdLength);
}
)
.MapEfCoreProperty<IdentityUser, int>(
nameof(AppUser.Sex),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasDefaultValue(0);
}
)
.MapEfCoreProperty<IdentityUser, int>(
nameof(AppUser.UserType),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasDefaultValue(0);
}
)
.MapEfCoreProperty<IdentityUser, int>(
nameof(AppUser.SchoolUserType),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasDefaultValue(0);
}
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.IDCard),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(AppUserConsts.MaxIDCardLength);
}
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.Avatar),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(AppUserConsts.MaxAvatarLength);
}
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.Title),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(AppUserConsts.MaxTitleLength);
}
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.Tags),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasConversion<string>();
}
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.Country),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(AppUserConsts.MaxCountryLength);
}
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.ProvinceId),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(AppUserConsts.MaxAreaCodeLength);
}
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.CityId),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(AppUserConsts.MaxAreaCodeLength);
}
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.DistrictId),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(AppUserConsts.MaxAreaCodeLength);
}
);
...
});
}
}
}
AppUserDbContext.cs
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
namespace IdentityExtensions.EntityFrameworkCore
{
[ConnectionStringName("Default")]
public class AppUserDbContext : AbpDbContext<AppUserDbContext>, IAppUserDbContext
{
public DbSet<AppUser> Users { get; set; }
/* Add DbSet properties for your Aggregate Roots / Entities here.
* Also map them inside UserManagementDbContextModelCreatingExtensions.ConfigureUserManagement
*/
public AppUserDbContext(DbContextOptions<AppUserDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Configure the shared tables (with included modules) here */
/* Configure your own tables/entities inside the ConfigureUserManagement method */
builder.ConfigureAppUser();
}
}
}
AppUserDbContextModelCreatingExtensions.cs
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using Volo.Abp;
using Volo.Abp.EntityFrameworkCore.Modeling;
using Volo.Abp.Identity;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.Users;
using Volo.Abp.Users.EntityFrameworkCore;
namespace IdentityExtensions.EntityFrameworkCore
{
public static class AppUserDbContextModelCreatingExtensions
{
public static void ConfigureAppUser(
[NotNull] this ModelBuilder builder,
[CanBeNull] Action<IdentityModelBuilderConfigurationOptions> optionsAction = null)
{
Check.NotNull(builder, nameof(builder));
builder.Entity<AppUser>(b =>
{
b.ToTable(AbpIdentityDbProperties.DbTablePrefix + "Users"); //Sharing the same table "AbpUsers" with the IdentityUser
b.ConfigureAbpUser();
b.ConfigureFullAuditedAggregateRoot();
b.ConfigureConcurrencyStamp();
b.HasOne<IdentityUser>().WithOne().HasForeignKey<AppUser>(i => i.Id);
b.ConfigureCustomUserProperties();
});
builder.Entity<IdentityUser>(b =>
{
b.ConfigureCustomUserProperties();
b.ApplyObjectExtensionMappings();
});
builder.Entity<IdentityUserLogin>().HasKey(x => new { x.UserId, x.LoginProvider });
builder.Entity<IdentityUserRole>().HasKey(x => new { x.UserId, x.RoleId });
builder.Entity<IdentityUserToken>().HasKey(x => new { x.UserId, x.LoginProvider });
builder.Entity<IdentityUserOrganizationUnit>().HasKey(x => new { x.UserId, x.OrganizationUnitId });
}
public static void ConfigureCustomUserProperties<TUser>(this EntityTypeBuilder<TUser> b) where TUser : class, IUser
{
b.Property<Guid?>(nameof(AppUser.UniqueId))
.HasColumnName(nameof(AppUser.UniqueId))
.HasComment("聚合唯一id");
b.Property<string>(nameof(AppUser.OuterId))
.HasColumnName(nameof(AppUser.OuterId))
.HasComment("外部id")
.HasMaxLength(AppUserConsts.MaxOuterIdLength);
b.Property<AppUserSex>(nameof(AppUser.Sex))
.HasColumnName(nameof(AppUser.Sex))
.HasComment("性别")
.HasConversion(
v => Convert.ToInt32(v),
v => (AppUserSex)Enum.Parse(typeof(AppUserSex), v.ToString()))
.HasDefaultValue((AppUserSex)0);
b.Property<AppUserType>(nameof(AppUser.UserType))
.HasColumnName(nameof(AppUser.UserType))
.HasComment("用户类型")
.HasConversion(
v => Convert.ToInt32(v),
v => (AppUserType)Enum.Parse(typeof(AppUserType), v.ToString()))
.HasDefaultValue((AppUserType)0);
b.Property<AppUserSchoolUserType>(nameof(AppUser.SchoolUserType))
.HasColumnName(nameof(AppUser.SchoolUserType))
.HasComment("校园用户类型")
.HasConversion(
v => Convert.ToInt32(v),
v => (AppUserSchoolUserType)Enum.Parse(typeof(AppUserSchoolUserType), v.ToString()))
.HasDefaultValue((AppUserSchoolUserType)0);
b.Property<string>(nameof(AppUser.IDCard))
.HasColumnName(nameof(AppUser.IDCard))
.HasComment("身份证号")
.HasMaxLength(AppUserConsts.MaxIDCardLength);
b.Property<string>(nameof(AppUser.Avatar))
.HasColumnName(nameof(AppUser.Avatar))
.HasComment("头像")
.HasMaxLength(AppUserConsts.MaxAvatarLength);
b.Property<string>(nameof(AppUser.Title))
.HasColumnName(nameof(AppUser.Title))
.HasComment("职称")
.HasMaxLength(AppUserConsts.MaxTitleLength);
b.Property<Dictionary<string, string>>(nameof(AppUser.Tags))
.HasColumnName(nameof(AppUser.Tags))
.HasComment("标签")
.HasConversion(
v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject<Dictionary<string, string>>(v));
b.Property<string>(nameof(AppUser.Country))
.HasColumnName(nameof(AppUser.Country))
.HasComment("国家")
.HasMaxLength(AppUserConsts.MaxCountryLength);
b.Property<string>(nameof(AppUser.ProvinceId))
.HasColumnName(nameof(AppUser.ProvinceId))
.HasComment("省份id")
.HasMaxLength(AppUserConsts.MaxAreaCodeLength);
b.Property<string>(nameof(AppUser.CityId))
.HasColumnName(nameof(AppUser.CityId))
.HasComment("城市id")
.HasMaxLength(AppUserConsts.MaxAreaCodeLength);
b.Property<string>(nameof(AppUser.DistrictId))
.HasColumnName(nameof(AppUser.DistrictId))
.HasComment("区县id")
.HasMaxLength(AppUserConsts.MaxAreaCodeLength);
...
}
}
}
AppUser.cs
public class AppUser : FullAuditedAggregateRoot<Guid>, IUser
{
#region Base properties
/* These properties are shared with the IdentityUser entity of the Identity module.
* Do not change these properties through this class. Instead, use Identity module
* services (like IdentityUserManager) to change them.
* So, this properties are designed as read only!
*/
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; }
#endregion
[NotMapped]
public override ExtraPropertyDictionary ExtraProperties { get; protected set; }
private AppUser()
{
}
...
https://community.abp.io/articles/how-to-add-custom-property-to-the-user-entity-6ggxiddr
这解决了我的问题。
private static void ConfigureExtraProperties()
{
ObjectExtensionManager.Instance.Modules().ConfigureIdentity(identity =>
{
identity.ConfigureUser(user =>
{
user.AddOrUpdateProperty<string>(
UserConsts.TitlePropertyName,
options =>
{
options.Attributes.Add(new RequiredAttribute());
options.Attributes.Add(
new StringLengthAttribute(UserConsts.MaxTitleLength)
);
}
);
user.AddOrUpdateProperty<int>(
UserConsts.ReputationPropertyName,
options =>
{
options.DefaultValue = UserConsts.MinReputationValue;
options.Attributes.Add(
new RangeAttribute(UserConsts.MinReputationValue, UserConsts.MaxReputationValue)
);
}
);
});
});
}
ABP框架版本:4.4.3 PostgreSQL: 13.4
异常
代码:
public async Task<IdentityUserDto> CreateAsync(IdentityUserCreateDto input)
{
await IdentityOptions.SetAsync();
_logger.Debug("创建用户-input\r\n" + JsonConvert.SerializeObject(input, Formatting.Indented));
var user = new Volo.Abp.Identity.IdentityUser(
GuidGenerator.Create(),
input.UserName,
input.Email,
CurrentTenant.Id
);
input.MapExtraPropertiesTo(user);
(await UserManager.CreateAsync(user, input.Password)).CheckErrors();
await UpdateUserByInput(user, input);
(await UserManager.UpdateAsync(user)).CheckErrors();
_logger.Debug("创建用户-user\r\n" + JsonConvert.SerializeObject(user, Formatting.Indented));
await CurrentUnitOfWork.SaveChangesAsync();
return ObjectMapper.Map<Volo.Abp.Identity.IdentityUser, IdentityUserDto>(user);
}
我用MapExtraPropertiesTo
把IdentityUserCreateDto
变成了IdentityUser
。但是 IdentityUser
的 属性 ExtraProperties
丢失了。
日志:
2021-11-22 08:57:13.008 +08:00 [DBG] 创建用户-input
{
"Password": "xxxxxxxx",
"UserName": "05150123",
"Name": "xxx",
"Surname": "xxx",
"Email": "05150123@56kad.com",
"PhoneNumber": null,
"LockoutEnabled": true,
"RoleNames": null,
"ExtraProperties": {
"Sex": 0,
"UserType": 2,
"SchoolUserType": 0,
"SchoolCode": "05150123",
"Country": "中国",
"ProvinceId": "320000000000",
"CityId": "320900000000",
"DistrictId": null
}
}
2021-11-22 08:57:13.303 +08:00 [DBG] 创建用户-user
{
"TenantId": null,
"UserName": "05150123",
"NormalizedUserName": "05150123",
"Name": "xxx",
"Surname": "xxx",
"Email": "05150123@56kad.com",
"NormalizedEmail": "05150123@56KAD.COM",
"EmailConfirmed": false,
"PasswordHash": "AQAAAAEAACcQAAAAEBdhpA4Y3azFxR0f2LCfE0W3vL5PvOBCzzxBTxsP63KGUnCSpdiHnAcL5diniThA==",
"SecurityStamp": "PU324ZRHDW7OO2SZHZUZ3Y6HF2H26U4P",
"IsExternal": false,
"PhoneNumber": null,
"PhoneNumberConfirmed": false,
"TwoFactorEnabled": false,
"LockoutEnd": null,
"LockoutEnabled": true,
"AccessFailedCount": 0,
"Roles": [],
"Claims": [],
"Logins": [],
"Tokens": [],
"OrganizationUnits": [],
"IsDeleted": false,
"DeleterId": null,
"DeletionTime": null,
"LastModificationTime": "2021-11-22T08:57:13.2831132+08:00",
"LastModifierId": "3a004944-b21f-0bbc-2376-44007c2a9295",
"CreationTime": "2021-11-22T08:57:13.1768976+08:00",
"CreatorId": "3a004944-b21f-0bbc-2376-44007c2a9295",
"ExtraProperties": {}, //lose
"ConcurrencyStamp": "08c5abb6ab55424899f40d30b275975b",
"Id": "3a005752-7630-f90f-3d3d-38da149eb416"
}
IdentityExtensionsEntityFrameworkCoreModule.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity;
namespace IdentityExtensions.EntityFrameworkCore
{
[DependsOn(
typeof(IdentityExtensionsDomainModule),
typeof(AbpEntityFrameworkCoreModule)
)]
public class IdentityExtensionsEntityFrameworkCoreModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
IdentityExtensionsEfCoreEntityExtensionMappings.Configure();
base.PreConfigureServices(context);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<AppUserDbContext>(options =>
{
/* Remove "includeAllEntities: true" to create
* default repositories only for aggregate roots */
options.AddDefaultRepositories();
});
context.Services.AddAbpDbContext<AggregateUniqueUserDbContext>(options =>
{
/* Remove "includeAllEntities: true" to create
* default repositories only for aggregate roots */
options.AddDefaultRepositories();
});
context.Services.Configure<AbpDbContextOptions>(options =>
{
options.UseNpgsql(b => b.UseNetTopologySuite());
});
}
}
}
IdentityExtensionsEfCoreEntityExtensionMappings.cs
using Microsoft.EntityFrameworkCore;
using System;
using Volo.Abp.Identity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Threading;
namespace IdentityExtensions.EntityFrameworkCore
{
public static class IdentityExtensionsEfCoreEntityExtensionMappings
{
private static readonly OneTimeRunner OneTimeRunner = new();
public static void Configure()
{
OneTimeRunner.Run(() =>
{
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, Guid?>(
nameof(AppUser.UniqueId),
(typeBuilder, propertyBuilder) => { }
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.OuterId),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(AppUserConsts.MaxOuterIdLength);
}
)
.MapEfCoreProperty<IdentityUser, int>(
nameof(AppUser.Sex),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasDefaultValue(0);
}
)
.MapEfCoreProperty<IdentityUser, int>(
nameof(AppUser.UserType),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasDefaultValue(0);
}
)
.MapEfCoreProperty<IdentityUser, int>(
nameof(AppUser.SchoolUserType),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasDefaultValue(0);
}
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.IDCard),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(AppUserConsts.MaxIDCardLength);
}
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.Avatar),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(AppUserConsts.MaxAvatarLength);
}
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.Title),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(AppUserConsts.MaxTitleLength);
}
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.Tags),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasConversion<string>();
}
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.Country),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(AppUserConsts.MaxCountryLength);
}
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.ProvinceId),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(AppUserConsts.MaxAreaCodeLength);
}
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.CityId),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(AppUserConsts.MaxAreaCodeLength);
}
)
.MapEfCoreProperty<IdentityUser, string>(
nameof(AppUser.DistrictId),
(typeBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(AppUserConsts.MaxAreaCodeLength);
}
);
...
});
}
}
}
AppUserDbContext.cs
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
namespace IdentityExtensions.EntityFrameworkCore
{
[ConnectionStringName("Default")]
public class AppUserDbContext : AbpDbContext<AppUserDbContext>, IAppUserDbContext
{
public DbSet<AppUser> Users { get; set; }
/* Add DbSet properties for your Aggregate Roots / Entities here.
* Also map them inside UserManagementDbContextModelCreatingExtensions.ConfigureUserManagement
*/
public AppUserDbContext(DbContextOptions<AppUserDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Configure the shared tables (with included modules) here */
/* Configure your own tables/entities inside the ConfigureUserManagement method */
builder.ConfigureAppUser();
}
}
}
AppUserDbContextModelCreatingExtensions.cs
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using Volo.Abp;
using Volo.Abp.EntityFrameworkCore.Modeling;
using Volo.Abp.Identity;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.Users;
using Volo.Abp.Users.EntityFrameworkCore;
namespace IdentityExtensions.EntityFrameworkCore
{
public static class AppUserDbContextModelCreatingExtensions
{
public static void ConfigureAppUser(
[NotNull] this ModelBuilder builder,
[CanBeNull] Action<IdentityModelBuilderConfigurationOptions> optionsAction = null)
{
Check.NotNull(builder, nameof(builder));
builder.Entity<AppUser>(b =>
{
b.ToTable(AbpIdentityDbProperties.DbTablePrefix + "Users"); //Sharing the same table "AbpUsers" with the IdentityUser
b.ConfigureAbpUser();
b.ConfigureFullAuditedAggregateRoot();
b.ConfigureConcurrencyStamp();
b.HasOne<IdentityUser>().WithOne().HasForeignKey<AppUser>(i => i.Id);
b.ConfigureCustomUserProperties();
});
builder.Entity<IdentityUser>(b =>
{
b.ConfigureCustomUserProperties();
b.ApplyObjectExtensionMappings();
});
builder.Entity<IdentityUserLogin>().HasKey(x => new { x.UserId, x.LoginProvider });
builder.Entity<IdentityUserRole>().HasKey(x => new { x.UserId, x.RoleId });
builder.Entity<IdentityUserToken>().HasKey(x => new { x.UserId, x.LoginProvider });
builder.Entity<IdentityUserOrganizationUnit>().HasKey(x => new { x.UserId, x.OrganizationUnitId });
}
public static void ConfigureCustomUserProperties<TUser>(this EntityTypeBuilder<TUser> b) where TUser : class, IUser
{
b.Property<Guid?>(nameof(AppUser.UniqueId))
.HasColumnName(nameof(AppUser.UniqueId))
.HasComment("聚合唯一id");
b.Property<string>(nameof(AppUser.OuterId))
.HasColumnName(nameof(AppUser.OuterId))
.HasComment("外部id")
.HasMaxLength(AppUserConsts.MaxOuterIdLength);
b.Property<AppUserSex>(nameof(AppUser.Sex))
.HasColumnName(nameof(AppUser.Sex))
.HasComment("性别")
.HasConversion(
v => Convert.ToInt32(v),
v => (AppUserSex)Enum.Parse(typeof(AppUserSex), v.ToString()))
.HasDefaultValue((AppUserSex)0);
b.Property<AppUserType>(nameof(AppUser.UserType))
.HasColumnName(nameof(AppUser.UserType))
.HasComment("用户类型")
.HasConversion(
v => Convert.ToInt32(v),
v => (AppUserType)Enum.Parse(typeof(AppUserType), v.ToString()))
.HasDefaultValue((AppUserType)0);
b.Property<AppUserSchoolUserType>(nameof(AppUser.SchoolUserType))
.HasColumnName(nameof(AppUser.SchoolUserType))
.HasComment("校园用户类型")
.HasConversion(
v => Convert.ToInt32(v),
v => (AppUserSchoolUserType)Enum.Parse(typeof(AppUserSchoolUserType), v.ToString()))
.HasDefaultValue((AppUserSchoolUserType)0);
b.Property<string>(nameof(AppUser.IDCard))
.HasColumnName(nameof(AppUser.IDCard))
.HasComment("身份证号")
.HasMaxLength(AppUserConsts.MaxIDCardLength);
b.Property<string>(nameof(AppUser.Avatar))
.HasColumnName(nameof(AppUser.Avatar))
.HasComment("头像")
.HasMaxLength(AppUserConsts.MaxAvatarLength);
b.Property<string>(nameof(AppUser.Title))
.HasColumnName(nameof(AppUser.Title))
.HasComment("职称")
.HasMaxLength(AppUserConsts.MaxTitleLength);
b.Property<Dictionary<string, string>>(nameof(AppUser.Tags))
.HasColumnName(nameof(AppUser.Tags))
.HasComment("标签")
.HasConversion(
v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject<Dictionary<string, string>>(v));
b.Property<string>(nameof(AppUser.Country))
.HasColumnName(nameof(AppUser.Country))
.HasComment("国家")
.HasMaxLength(AppUserConsts.MaxCountryLength);
b.Property<string>(nameof(AppUser.ProvinceId))
.HasColumnName(nameof(AppUser.ProvinceId))
.HasComment("省份id")
.HasMaxLength(AppUserConsts.MaxAreaCodeLength);
b.Property<string>(nameof(AppUser.CityId))
.HasColumnName(nameof(AppUser.CityId))
.HasComment("城市id")
.HasMaxLength(AppUserConsts.MaxAreaCodeLength);
b.Property<string>(nameof(AppUser.DistrictId))
.HasColumnName(nameof(AppUser.DistrictId))
.HasComment("区县id")
.HasMaxLength(AppUserConsts.MaxAreaCodeLength);
...
}
}
}
AppUser.cs
public class AppUser : FullAuditedAggregateRoot<Guid>, IUser
{
#region Base properties
/* These properties are shared with the IdentityUser entity of the Identity module.
* Do not change these properties through this class. Instead, use Identity module
* services (like IdentityUserManager) to change them.
* So, this properties are designed as read only!
*/
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; }
#endregion
[NotMapped]
public override ExtraPropertyDictionary ExtraProperties { get; protected set; }
private AppUser()
{
}
...
https://community.abp.io/articles/how-to-add-custom-property-to-the-user-entity-6ggxiddr
这解决了我的问题。
private static void ConfigureExtraProperties()
{
ObjectExtensionManager.Instance.Modules().ConfigureIdentity(identity =>
{
identity.ConfigureUser(user =>
{
user.AddOrUpdateProperty<string>(
UserConsts.TitlePropertyName,
options =>
{
options.Attributes.Add(new RequiredAttribute());
options.Attributes.Add(
new StringLengthAttribute(UserConsts.MaxTitleLength)
);
}
);
user.AddOrUpdateProperty<int>(
UserConsts.ReputationPropertyName,
options =>
{
options.DefaultValue = UserConsts.MinReputationValue;
options.Attributes.Add(
new RangeAttribute(UserConsts.MinReputationValue, UserConsts.MaxReputationValue)
);
}
);
});
});
}