This backend code execute not slow: RegisterLocalUserAsync3CreateTenant()
The slow is Angular UI. When register successfully, it need 15+ seconds to return the first page.
public class MyRegisterModel : RegisterModel {
[BindProperty] public MyTenantTableInput InputTenantType { get; set; }
[BindProperty] public new MyPostInput Input { get; set; }
private ITenantRepository TenantRepository { get; }
protected ITenantManager _tenantManager { get; }
protected IMyLoginDomainManager _myLoginDomainManager { get; }
protected IIdentityRoleRepository _roleRepository { get; }
protected IIdentityUserRepository _userRepository { get; }
protected ILookupNormalizer LookupNormalizer { get; }
protected IdentityUserManager UserManager { get; }
protected IdentityRoleManager RoleManager { get; }
public MyRegisterModel(ITenantRepository tenantRepository, ITenantManager tenantManager,
IMyLoginDomainManager myLoginDomainManager,
IIdentityRoleRepository identityRoleRepository ,
IIdentityUserRepository identityUserRepository,
ILookupNormalizer lookupNormalizer ,
IdentityUserManager userManager,
IdentityRoleManager roleManager
)
{
LookupNormalizer = lookupNormalizer;
_roleRepository = identityRoleRepository;
_userRepository = identityUserRepository;
_myLoginDomainManager = myLoginDomainManager;
_tenantManager = tenantManager;
TenantRepository = tenantRepository;
UserManager = userManager;
RoleManager = roleManager;
}
public override async Task<IActionResult> OnGetAsync()
{
InputTenantType = new MyTenantTableInput();
InputTenantType.MyTenantType = (int) TenantTypes.Buyer;
return await base.OnGetAsync();
}
[UnitOfWork]
public override async Task<IActionResult> OnPostAsync()
{
try
{
await CheckSelfRegistrationAsync();
await SetUseCaptchaAsync();
IdentityUser user;
if (IsExternalLogin)
{
var externalLoginInfo = await SignInManager.GetExternalLoginInfoAsync();
if (externalLoginInfo == null)
{
Logger.LogWarning("External login info is not available");
return RedirectToPage("./Login");
}
user = await RegisterExternalUserAsync(externalLoginInfo, Input.EmailAddress);
}
else
{
var localLoginResult = await CheckLocalLoginAsync();
if (localLoginResult != null)
{
LocalLoginDisabled = true;
return localLoginResult;
}
user = await RegisterLocalUserAsync3CreateTenant();
}
if (await SettingProvider.IsTrueAsync(IdentitySettingNames.SignIn.RequireConfirmedEmail) &&
!user.EmailConfirmed ||
await SettingProvider.IsTrueAsync(IdentitySettingNames.SignIn.RequireConfirmedPhoneNumber) &&
!user.PhoneNumberConfirmed)
{
await StoreConfirmUser(user);
return RedirectToPage("./ConfirmUser", new
{
returnUrl = ReturnUrl,
returnUrlHash = ReturnUrlHash
});
}
await SignInManager.SignInAsync(user, isPersistent: true);
return
Redirect(ReturnUrl ??
"/"); //TODO: How to ensure safety? IdentityServer requires it however it should be checked somehow!
}
catch (BusinessException e)
{
Alerts.Danger(GetLocalizeExceptionMessage(e));
return Page();
}
}
protected virtual async Task<IdentityUser> RegisterLocalUserAsync3CreateTenant()
{
ValidateModel();
var tenantObj = new SaasTenantCreateDto()
{
ActivationState = Saas.TenantActivationState.Active,
AdminEmailAddress = "m@m.m", //host email
AdminPassword = "1q2w3E*", // host pwd
Name = Input.EmailAddress.ToLower().Trim(),
};
tenantObj.ExtraProperties.Add("TenantType", InputTenantType.MyTenantType);
var tenant = await _tenantManager.CreateAsync(tenantObj.Name);
tenantObj.MapExtraPropertiesTo(tenant);
await TenantRepository.InsertAsync(tenant);
await CurrentUnitOfWork.SaveChangesAsync();
await _myLoginDomainManager.MyTenantCreateDoneAsync(tenant, tenantObj.AdminEmailAddress, tenantObj.AdminPassword);
if(tenant != null)
{
const string adminUserName = "supermanager";
const string adminRoleName = "supermanager";
string tenantemail = Input.EmailAddress.ToLower().Trim();
string tenantpwd = Input.Password.ToLower().Trim();
using (CurrentTenant.Change(tenant.Id))
{
var adminUser = await _userRepository.FindByNormalizedUserNameAsync(
LookupNormalizer.NormalizeName(adminUserName));
if (adminUser == null)
{
var adminRole = await _roleRepository.FindByNormalizedNameAsync(LookupNormalizer.NormalizeName(adminRoleName));
if (adminRole == null)
{
adminRole = new IdentityRole(
GuidGenerator.Create(),
adminRoleName,
tenant.Id
)
{
IsStatic = true,
IsPublic = true,
IsDefault = true
};
adminRole.ExtraProperties.Add("RoleType", (int)RoleTypes.SuperManagerLevel);
(await RoleManager.CreateAsync(adminRole)).CheckErrors();
}
var userObj = new RegisterDto
{
AppName = "MVC",
EmailAddress = Input.EmailAddress,
Password = Input.Password,
UserName = Input.EmailAddress,
ReturnUrl = ReturnUrl,
ReturnUrlHash = ReturnUrlHash
};
userObj.ExtraProperties.Add("RoleType", (int)RoleTypes.SuperManagerLevel);
userObj.ExtraProperties.Add("TenantType", ((int)InputTenantType.MyTenantType).ToString());
IdentityUserDto userDto = await AccountAppService.RegisterAsync(userObj);
await CurrentUnitOfWork.SaveChangesAsync();
var user = await UserManager.GetByIdAsync(userDto.Id);
return user;
}
}
}
return null;
}
public class MyTenantTableInput
{
public int MyTenantType { get; set; }
public List<SelectListItem> TenantTypeItems { get; } = new List<SelectListItem>
{
new SelectListItem {Value = ((int) TenantTypes.Buyer).ToString(), Text = "Buyer"},
new SelectListItem {Value = ((int) TenantTypes.Seller).ToString(), Text = "Seller"},
};
}
public class MyPostInput
{
[Required]
[EmailAddress]
[DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxEmailLength))]
public string EmailAddress { get; set; }
[Required]
[DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))]
[DataType(DataType.Password)]
[DisableAuditing]
public string Password { get; set; }
}
}
Hi,
Yes, the steps are different with yours. It is about the register + login. And do not create tenant on page. Every register user (put in email) will create new tenant in backend.
Steps: 1, run Angular + ef app 2, go to login page, and click the register link to register page 3, put in email + pwd to register 4, create new tenant which tenant name is the put in email 5, register the putin email into this new tenant. 6, signin the new user and go back angular UI.
== =======================================================================
Detail I create custom register logic in this class. MyRegisterModel : RegisterModel
Here are my **steps **(logic):
1, Create Angular+ef app 2, create new file follow the custom login page: Create MyRegisterModel : RegisterModel 3, run the app 4, go to login page 5, click register link to register page 6, put in email and pwd
7, call this backend code RegisterLocalUserAsync3CreateTenant() of MyRegisterModel : RegisterModel
8, it will create Tenant with put in email as tenant name. (admin user is my hardcode another host email) 9, Using this new created tenant: using (CurrentTenant.Change(tenant.Id)) 10, create one general role for the new tenant. 11, register general user with put in email + pwd for the new tenant.
12, after the user created in the new tenant 13, it will call SignInManager.SignInAsync() 14, then Redirect(ReturnUrl ?? "/");
Then angular client will wait for about 15 seconds to refresh the page. And Result is correct. the put in email user will login the new tenant.
Please see my code for the Register.cshtml.cs
Requirement: when user register with email, it will create a tenant with his email. And same time, user will signin with this email under the new tenant created by his email.
Logic: 1, User register with Email and pwd. 2, create new tenant with this email. and create admin user for this tenant. 3, after step 2 success, it will call AccountAppService.RegisterAsync() to register into this new tenant. 4, var user = await UserManager.GetByIdAsync(userDto.Id); 5, await SignInManager.SignInAsync(user, isPersistent: true); 6, return Redirect(ReturnUrl ?? "/");
All steps works and successed. and speed is quick.
But The question is, the angular UI redirect is slow. When the user success pass the six steps and goback to Angular UI. The Angular UI need about 15+ seconds to redirect to the correct page.
How should I do to make Angular UI quick redirect? Is IDS4 auth cause it slow? Or any other wrong? The backend code is fast.
thx
Hi,
Before v4+ works for this way to change login page. Right now, I still use this method to change the login page. It seems <abp-input> do not show out when customizing login page.
Follow: https://community.abp.io/posts/how-to-customize-the-login-page-for-mvc-razor-page-applications-9a40f3cd https://docs.abp.io/en/abp/latest/UI/AspNetCore/Customization-User-Interface