Hi
I have created a method similar to the tenant registration page but when i run the code and it breaks after the first changes the transaction dosen't role back.
Register in controller:
[HttpPost]
[UnitOfWork]
public virtual async Task<ActionResult> Register(WorkerRegistrationViewModel model)
{
try
{
if (UseCaptchaOnRegistration())
{
var recaptchaHelper = this.GetRecaptchaVerificationHelper();
if (recaptchaHelper.Response.IsNullOrEmpty())
{
throw new UserFriendlyException(L("CaptchaCanNotBeEmpty"));
}
if (recaptchaHelper.VerifyRecaptchaResponse() != RecaptchaVerificationResult.Success)
{
throw new UserFriendlyException(L("IncorrectCaptchaAnswer"));
}
}
//Getting host-specific settings
var isNewRegisteredTenantActiveByDefault = await SettingManager.GetSettingValueForApplicationAsync<bool>(AppSettings.TenantManagement.IsNewRegisteredTenantActiveByDefault);
var isEmailConfirmationRequiredForLogin = await SettingManager.GetSettingValueForApplicationAsync<bool>(AbpZeroSettingNames.UserManagement.IsEmailConfirmationRequiredForLogin);
var defaultEditionIdValue = await SettingManager.GetSettingValueForApplicationAsync(AppSettings.TenantManagement.DefaultEdition);
Guid? defaultEditionId = null;
if (!string.IsNullOrEmpty(defaultEditionIdValue) && (await _editionManager.FindByIdAsync(Guid.Parse(defaultEditionIdValue)) != null))
{
defaultEditionId = Guid.Parse(defaultEditionIdValue);
}
Guid tenantId = AbpSession.TenantId.Value;
CurrentUnitOfWork.SetTenantId(tenantId);
ViewBag.UseCaptcha = UseCaptchaOnRegistration();
var tenant = await _tenantManager.GetByIdAsync(tenantId);
DateTime? dateOfBirth = null;
try
{
dateOfBirth = DateTime.Parse($"{model.Dob_Year}-{model.Dob_Month}-{model.Dob_Day}");
}
catch (Exception)
{
throw new UserFriendlyException(L("DateOfBirthFormatError"));
}
var userId = await _candidateManager.CreateCandidateAsync(tenantId,
model.UserName,
model.FirstName,
model.LastName,
dateOfBirth.Value,
model.Password,
model.EmailAddress,
true);
var user = await _userManager.FindByIdAsync(userId);
//Directly login if possible
if (tenant.IsActive && user.IsActive && (user.IsEmailConfirmed || !isEmailConfirmationRequiredForLogin))
{
var loginResult = await GetLoginResultAsync(user.UserName, model.Password, tenant.TenancyName);
if (loginResult.Result == AbpLoginResultType.Success)
{
await SignInAsync(loginResult.User, loginResult.Identity);
return Redirect(Url.Action("Index", "Application"));
}
Logger.Warn("New registered user could not be login. This should not be normally. login result: " + loginResult.Result);
}
//await _appNotifier.NewTenantRegisteredAsync(tenant);
return View("RegisterResult", new WorkerRegisterResultViewModel
{
TenancyName = tenant.TenancyName,
Name = model.FirstName,
UserName = Authorization.Users.User.AdminUserName,
EmailAddress = model.EmailAddress,
IsActive = isNewRegisteredTenantActiveByDefault,
IsEmailConfirmationRequired = isEmailConfirmationRequiredForLogin
});
}
catch (UserFriendlyException ex)
{
ViewBag.UseCaptcha = UseCaptchaOnRegistration();
ViewBag.ErrorMessage = ex.Message;
return View("Index", model);
}
}
I have wrote this code to create the candidate
public async Task<Guid> CreateCandidateAsync(Guid tenantId, string userName, string firstName, string lastName, DateTime dateOfBirth, string password, string emailAddress, bool isActive, Guid? editionId = null, bool shouldChangePasswordOnNextLogin = false, bool sendActivationEmail = false)
{
Guid newUserId;
using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew))
{
using (_unitOfWorkManager.Current.SetTenantId(tenantId))
{
//Create admin user for the tenant
if (password.IsNullOrEmpty())
{
password = User.CreateRandomPassword();
}
var user = User.CreateCandidateUser(tenantId, userName, firstName, lastName, emailAddress, password);
user.ShouldChangePasswordOnNextLogin = shouldChangePasswordOnNextLogin;
user.IsActive = isActive;
CheckErrors(await _userManager.CreateAsync(user));
await _unitOfWorkManager.Current.SaveChangesAsync(); //To get candidate user's id
var userRole = _roleManager.Roles.Single(r => r.Name == StaticRoleNames.Tenants.Candidate);
//Assign admin user to admin role!
CheckErrors(await _userManager.AddToRoleAsync(user.Id, userRole.Name));
var ca = new Candidate();
ca.Id = user.Id;
ca.TenantId = tenantId;
ca.Number = 1;
ca.FirstName = firstName;
ca.LastName = lastName;
ca.Address = new Address();
ca.MobileNumber = new PhoneNumber();
ca.PhoneNumber = new PhoneNumber();
await _candidateRepository.InsertAsync(ca);
await _unitOfWorkManager.Current.SaveChangesAsync(); //To get candidate's id
<span style="color:#FF0000">//Is is here the exception is thrown</span>
//Notifications
await _appNotifier.WelcomeToTheApplicationAsync(user);
//Send activation email
if (sendActivationEmail)
{
user.SetNewEmailConfirmationCode();
await _userEmailer.SendEmailActivationLinkAsync(user, password);
}
await _unitOfWorkManager.Current.SaveChangesAsync();
newUserId = user.Id;
}
await uow.CompleteAsync();
}
//Used a second UOW since UOW above sets some permissions and _notificationSubscriptionManager.SubscribeToAllAvailableNotificationsAsync needs these permissions to be saved.
using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew))
{
using (_unitOfWorkManager.Current.SetTenantId(tenantId))
{
await _notificationSubscriptionManager.SubscribeToAllAvailableNotificationsAsync(new UserIdentifier(tenantId, newUserId));
await _unitOfWorkManager.Current.SaveChangesAsync();
await uow.CompleteAsync();
}
}
return newUserId;
}
Hope somone can help.
3 Answer(s)
-
0
Hi,
Since you use a try-catch block in your controller and don't handle uow in your catch block, this is the expected behavior. If you want to use a try-catch block, you can disable UOW for your controller (using it's attribute [UnitOfWork(IsDisabled = true)]), and then manually start a new UOW in your controller. In this way, you can rollback this transaction in your catch block.
-
0
Thanks for the answer.
I have used the method Register from TenantRegistrationController, would this code not role back either?
-
0
Hi,
We will also test this and fix if there is a problem. <a class="postlink" href="https://github.com/aspnetzero/aspnet-zero/issues/429">https://github.com/aspnetzero/aspnet-zero/issues/429</a>. Probably same thing will happen.