使用ABPzero的朋友們都知道,User表中有Name和Surname兩個字段,這兩個字段對於國內的用戶來說相當的不友好。
以及我們的一些系統中是不會涉及到EmailAddress字段。也就是說不會使用郵箱來進行注冊的,那么我們怎么解決了。
首先感謝群友https://github.com/maliming 提供的思路。
- 在abpzero中的AbpUser實體中Name和Surname、EmailAddress都是為必填。
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Abp.Configuration;
using Abp.Domain.Entities;
using Abp.Domain.Entities.Auditing;
using Abp.Extensions;
namespace Abp.Authorization.Users
{
/// <summary>
/// Represents a user.
/// </summary>
public abstract class AbpUser<TUser> : AbpUserBase, IFullAudited<TUser>, IPassivable
where TUser : AbpUser<TUser>
{
/// <summary>
/// UserName of the admin.
/// admin can not be deleted and UserName of the admin can not be changed.
/// </summary>
public const string AdminUserName = "admin";
/// <summary>
/// Maximum length of the <see cref="Name"/> property.
/// </summary>
public const int MaxNameLength = 32;
/// <summary>
/// Maximum length of the <see cref="Surname"/> property.
/// </summary>
public const int MaxSurnameLength = 32;
/// <summary>
/// Maximum length of the <see cref="Password"/> property.
/// </summary>
public const int MaxPasswordLength = 128;
/// <summary>
/// Maximum length of the <see cref="Password"/> without hashed.
/// </summary>
public const int MaxPlainPasswordLength = 32;
/// <summary>
/// Maximum length of the <see cref="EmailConfirmationCode"/> property.
/// </summary>
public const int MaxEmailConfirmationCodeLength = 328;
/// <summary>
/// Maximum length of the <see cref="PasswordResetCode"/> property.
/// </summary>
public const int MaxPasswordResetCodeLength = 328;
/// <summary>
/// Maximum length of the <see cref="AuthenticationSource"/> property.
/// </summary>
public const int MaxAuthenticationSourceLength = 64;
/// <summary>
/// Authorization source name.
/// It's set to external authentication source name if created by an external source.
/// Default: null.
/// </summary>
[MaxLength(MaxAuthenticationSourceLength)]
public virtual string AuthenticationSource { get; set; }
/// <summary>
/// Name of the user.
/// </summary>
[Required]
[StringLength(MaxNameLength)]
public virtual string Name { get; set; }
/// <summary>
/// Surname of the user.
/// </summary>
[Required]
[StringLength(MaxSurnameLength)]
public virtual string Surname { get; set; }
/// <summary>
/// Return full name (Name Surname )
/// </summary>
[NotMapped]
public virtual string FullName { get { return this.Name + " " + this.Surname; } }
/// <summary>
/// Password of the user.
/// </summary>
[Required]
[StringLength(MaxPasswordLength)]
public virtual string Password { get; set; }
/// <summary>
/// Is the <see cref="AbpUserBase.EmailAddress"/> confirmed.
/// </summary>
public virtual bool IsEmailConfirmed { get; set; }
/// <summary>
/// Confirmation code for email.
/// </summary>
[StringLength(MaxEmailConfirmationCodeLength)]
public virtual string EmailConfirmationCode { get; set; }
/// <summary>
/// Reset code for password.
/// It's not valid if it's null.
/// It's for one usage and must be set to null after reset.
/// </summary>
[StringLength(MaxPasswordResetCodeLength)]
public virtual string PasswordResetCode { get; set; }
/// <summary>
/// Lockout end date.
/// </summary>
public virtual DateTime? LockoutEndDateUtc { get; set; }
/// <summary>
/// Gets or sets the access failed count.
/// </summary>
public virtual int AccessFailedCount { get; set; }
/// <summary>
/// Gets or sets the lockout enabled.
/// </summary>
public virtual bool IsLockoutEnabled { get; set; }
/// <summary>
/// Gets or sets the phone number.
/// </summary>
public virtual string PhoneNumber {get; set; }
/// <summary>
/// Is the <see cref="AbpUser{TUser}.PhoneNumber"/> confirmed.
/// </summary>
public virtual bool IsPhoneNumberConfirmed { get; set; }
/// <summary>
/// Gets or sets the security stamp.
/// </summary>
public virtual string SecurityStamp { get; set; }
/// <summary>
/// Is two factor auth enabled.
/// </summary>
public virtual bool IsTwoFactorEnabled { get; set; }
/// <summary>
/// Is this user active?
/// If as user is not active, he/she can not use the application.
/// </summary>
public virtual bool IsActive { get; set; }
/// <summary>
/// Login definitions for this user.
/// </summary>
[ForeignKey("UserId")]
public virtual ICollection<UserLogin> Logins { get; set; }
/// <summary>
/// Roles of this user.
/// </summary>
[ForeignKey("UserId")]
public virtual ICollection<UserRole> Roles { get; set; }
/// <summary>
/// Claims of this user.
/// </summary>
[ForeignKey("UserId")]
public virtual ICollection<UserClaim> Claims { get; set; }
/// <summary>
/// Permission definitions for this user.
/// </summary>
[ForeignKey("UserId")]
public virtual ICollection<UserPermissionSetting> Permissions { get; set; }
/// <summary>
/// Settings for this user.
/// </summary>
[ForeignKey("UserId")]
public virtual ICollection<Setting> Settings { get; set; }
public virtual TUser DeleterUser { get; set; }
public virtual TUser CreatorUser { get; set; }
public virtual TUser LastModifierUser { get; set; }
protected AbpUser()
{
IsActive = true;
IsLockoutEnabled = true;
SecurityStamp = SequentialGuidGenerator.Instance.Create().ToString();
}
public virtual void SetNewPasswordResetCode()
{
PasswordResetCode = Guid.NewGuid().ToString("N").Truncate(MaxPasswordResetCodeLength);
}
public virtual void SetNewEmailConfirmationCode()
{
EmailConfirmationCode = Guid.NewGuid().ToString("N").Truncate(MaxEmailConfirmationCodeLength);
}
public override string ToString()
{
return string.Format("[User {0}] {1}", Id, UserName);
}
}
}
以上為ABPZERO源代碼中的字段,我們怎么修改呢。
- 修改User實體信息。
打開我們項目中的User.cs實體。
public class User : AbpUser<User>
{
public const string DefaultPassword = "123qwe";
public static string CreateRandomPassword()
{
return Guid.NewGuid().ToString("N").Truncate(16);
}
private new string Name { get; set; }
private new string Surname { get; set; }
[Required(AllowEmptyStrings = true)]
public override string EmailAddress { get; set; }
public static User CreateTenantAdminUser(int tenantId, string emailAddress, string password)
{
return new User
{
TenantId = tenantId,
UserName = AdminUserName,
Name = AdminUserName,
Surname = AdminUserName,
EmailAddress = emailAddress,
Password = new PasswordHasher().HashPassword(password)
};
}
}
可以看到我將Name和SurName字段設置為private,這樣設置之后,就有了值對象的感覺,EF在做實體驗證的時候就不會對private的屬性字段進行驗證。
而Emailaddress因為涉及了很多的業務情況,我們不能將它設置為私有訪問。
- 修改DbContext
我們打開'CMSDbContext.cs',添加方法
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>().Ignore(a => a.Name);
modelBuilder.Entity<User>().Ignore(a => a.Surname);
modelBuilder.Entity<User>().Property(a => a.EmailAddress).IsOptional();
// modelBuilder.Entity<User>().a => a.Name);
}
覆蓋方法‘OnModelCreating’,然后將Name和SurName設置為忽略。
然后將EmailAddress設置為可空。
- 執行遷移
然后執行遷移文件命令:"add-migration Remove_Name_SurName".
然后再執行"update-database",生成的數據庫中就不會有“Name”和“Surname”。 - 到目前為止,Name和SurName就不影響功能使用和開發了。
EmailAddress實體字段修改
CheckErrors(await _userManager.CreateAsync(user));
在userManager領域服務中的CreateAsync提供的方法中,檢查了EmailAddress 所以我們要重寫方法。
修改后的'UserManager.cs'
public class UserManager : AbpUserManager<Role, User>
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
public UserManager(
UserStore userStore,
RoleManager roleManager,
IPermissionManager permissionManager,
IUnitOfWorkManager unitOfWorkManager,
ICacheManager cacheManager,
IRepository<OrganizationUnit, long> organizationUnitRepository,
IRepository<UserOrganizationUnit, long> userOrganizationUnitRepository,
IOrganizationUnitSettings organizationUnitSettings,
ILocalizationManager localizationManager,
ISettingManager settingManager,
IdentityEmailMessageService emailService,
IUserTokenProviderAccessor userTokenProviderAccessor, IUnitOfWorkManager unitOfWorkManager1)
: base(
userStore,
roleManager,
permissionManager,
unitOfWorkManager,
cacheManager,
organizationUnitRepository,
userOrganizationUnitRepository,
organizationUnitSettings,
localizationManager,
emailService,
settingManager,
userTokenProviderAccessor)
{
_unitOfWorkManager = unitOfWorkManager1;
}
public override async Task<IdentityResult> CheckDuplicateUsernameOrEmailAddressAsync(long? expectedUserId, string userName, string emailAddress)
{
var user = (await FindByNameAsync(userName));
if (user != null && user.Id != expectedUserId)
{
return AbpIdentityResult.Failed(string.Format(L("Identity.DuplicateName"), userName));
}
return IdentityResult.Success;
}
private string L(string name)
{
return LocalizationManager.GetString(AbpZeroConsts.LocalizationSourceName, name);
}
public override async Task<IdentityResult> CreateAsync(User user)
{
var result = await CheckDuplicateUsernameOrEmailAddressAsync(user.Id, user.UserName, user.EmailAddress);
if (!result.Succeeded)
{
return result;
}
user.EmailAddress = string.Empty;
var tenantId = GetCurrentTenantId();
if (tenantId.HasValue && !user.TenantId.HasValue)
{
user.TenantId = tenantId.Value;
}
try
{
return await base.CreateAsync(user);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
private int? GetCurrentTenantId()
{
if (_unitOfWorkManager.Current != null)
{
return _unitOfWorkManager.Current.GetTenantId();
}
return AbpSession.TenantId;
}
}
截止以上我們的功能EmailAddress功能就算正常了。
以下為舊文,大家可以自己對比觀察。
使用ABPzero的朋友們都知道,User表中有Name和Surname兩個字段,這兩個字段對於國內的用戶來說相當的不友好。
我們在嘗試了很多的方法之后,發現無法完美將他們干掉。
所以嘗試使用了一個比較不友好加流氓的方式來使用它。
如果你在使用的過程有更加美好的解決方案,可以提供給我們。
以及到https://github.com/aspnetboilerplate/module-zero/issues/337 這里提出方法。
開始流程:
首先到 "CMSDbContext.cs"中添加覆蓋方法,找不到的如圖所示:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>().Ignore(a => a.Name);
modelBuilder.Entity<User>().Ignore(a => a.Surname);
}
然后執行遷移文件命令:"add-migration Remove_Name_SurName".
然后再執行"update-database",生成的數據庫中就不會有“Name”和“Surname”。
但是坑爹的地方就在這,我們在執行Insert(User)方法的時候還是會報錯。對於這種情況。我深深的感覺到了無力(感覺身體被Kid掏空)。
沒有其他辦法,只能暴力解決。在方法中,默認添加Name和Surname的值,只是為了繞過Eentityframework的實體驗證。。
如下圖所示。
這樣繞過之后,發現可以正常的處理信息。
User表中的Name和Surname也不在了。唯一不友好的地方就是坑爹。。
以上算是一個坑爹解決方法。
如果你有更加友好的解決方法。聯系我。