public async Task <CreateCompanyTxn_Output> Execute(CreateCompanyTxn_Input input, IServiceProvider serviceProvider, MVCDbContext contextFatherMVC = null, ApplicationDbContext contextFatherApp = null, bool autoCommit = true) { CreateCompanyTxn_Output output = new CreateCompanyTxn_Output(); output.ResultConfirmation = resultConfirmation.resultBad(_ResultMessage: "TXN_NOT_STARTED"); String userType = "Company"; // Error handling bool error = false; // To Handle Only One Error try { ApplicationDbContext contextApp = (contextFatherApp != null) ? contextFatherApp : new ApplicationDbContext(); MVCDbContext contextMVC = (contextFatherMVC != null) ? contextFatherMVC : new MVCDbContext(); // An using statement is in reality a try -> finally statement, disposing the element in the finally. So we need to take advance of that to create a DBContext inheritance try { // DBContext by convention is a UnitOfWork, track changes and commits when SaveChanges is called // Multithreading issue: so if we use only one DBContext, it will track all entities (_context.Customer.Add) and commit them when SaveChanges is called, // No Matter what transactions or client calls SaveChanges. // Note: Rollback will not remove the entities from the tracker in the context. so better dispose it. //***** 0. Make The Validations - Be careful : Concurrency. Same name can be saved multiple times if called at the exact same time. Better have an alternate database constraint UserManager <IdentityUser> _userManager = serviceProvider.GetService <UserManager <IdentityUser> >(); var user = new IdentityUser { UserName = input.Company.name, Email = input.Company.email, PasswordHash = input.Company.password }; var result = await _userManager.CreateAsync(user); if (!result.Succeeded) { error = true; output.ResultConfirmation = resultConfirmation.resultBad(_ResultMessage: "COMPANY_NOT_CREATED_ERROR", _ResultDetail: result.Errors.FirstOrDefault().Description); // If OK List <itemKey> resultKeys = new List <itemKey>(); foreach (var errorDesc in result.Errors) { resultKeys.Add(new itemKey(errorDesc.Code, errorDesc.Description)); } output.ResultConfirmation.resultDictionary = resultKeys; } if (!error) { //***** 1. Create the token (Atomic because is same Context DB) // Save the Name Claim into the database var _claims = new[] { new Claim(ClaimTypes.Name, user.Id) , new Claim("UserType", userType) // Create the token without Manager status, then add the claim in database }; var resultClaims = await _userManager.AddClaimsAsync(user, _claims); // Save claims in the Database // Call transaction to get a new Token (we call the Mutation not the class itself) var tokenString = ""; CreateTokenTxn_Input createTokenTxn_Input = new CreateTokenTxn_Input { userAppId = user.Id }; CreateTokenTxn_Output createTokenTxn = await new GraphQLMutation().CreateTokenTxn(input: createTokenTxn_Input, serviceProvider: serviceProvider); if (createTokenTxn.resultConfirmation.resultPassed) { tokenString = createTokenTxn.token; } input.Company.companyId = user.Id; input.Company.password = ""; input.Company.tokenAuth = tokenString; contextMVC.Company.Add(input.Company); //***** 4. Save and Commit to the Database (Atomic because is same Context DB) if (!error && autoCommit) { await contextMVC.SaveChangesAsync(); // Call it only once so do all other operations first await contextApp.SaveChangesAsync(); // Call it only once so do all other operations first } //***** 5. Execute Send e-mails or other events once the database has been succesfully saved //***** If this task fails, there are options -> 1. Retry multiple times 2. Save the event as Delay, 3.Rollback Database, Re // Create the Login Token for the company try { if (!error) { input.Company.loginTokenId = LoginTokenFunction.CreateTokenId(input.Company.id, DateTime.UtcNow); contextMVC.Company.Update(input.Company); await contextMVC.SaveChangesAsync(); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine("Exception: " + ex.Message); } //***** 6. Confirm the Result (Pass | Fail) If gets to here there are not errors then return the new data from database output.ResultConfirmation = resultConfirmation.resultGood(_ResultMessage: "COMPANY_SUCESSFULLY_CREATED"); // If OK //output.token = tokenString; // The token output.Company = input.Company; }// if (!error) } finally { // If the context Father is null the context was created on his own, so dispose it if (contextApp != null && contextFatherApp == null) { contextApp.Dispose(); } if (contextMVC != null && contextFatherMVC == null) { contextMVC.Dispose(); } } } catch (Exception ex) // Main try { System.Diagnostics.Debug.WriteLine("Error: " + ex.Message); string innerError = (ex.InnerException != null) ? ex.InnerException.Message : ""; System.Diagnostics.Debug.WriteLine("Error Inner: " + innerError); output = new CreateCompanyTxn_Output(); // Restart variable to avoid returning any already saved data output.ResultConfirmation = resultConfirmation.resultBad(_ResultMessage: "EXCEPTION", _ResultDetail: ex.Message); } finally { // Save Logs if needed } return(output); }
public async Task <CreateTokenTxn_Output> Execute(CreateTokenTxn_Input input, IServiceProvider serviceProvider, ApplicationDbContext contextFather = null, bool autoCommit = true) { CreateTokenTxn_Output _output = new CreateTokenTxn_Output(); _output.resultConfirmation = resultConfirmation.resultBad(_ResultMessage: "TXN_NOT_STARTED"); // Error handling bool error = false; // To Handle Only One Error try { ApplicationDbContext contextMGT = (contextFather != null) ? contextFather : new ApplicationDbContext(); // An using statement is in reality a try -> finally statement, disposing the element in the finally. So we need to take advance of that to create a DBContext inheritance try { // DBContext by convention is a UnitOfWork, track changes and commits when SaveChanges is called // Multithreading issue: so if we use only one DBContext, it will track all entities (_context.Customer.Add) and commit them when SaveChanges is called, // No Matter what transactions or client calls SaveChanges. // Note: Rollback will not remove the entities from the tracker in the context. so better dispose it. //***** 0. Make The Validations - Be careful : Concurrency. Same name can be saved multiple times if called at the exact same time. Better have an alternate database constraint UserManager <IdentityUser> userManager = serviceProvider.GetService <UserManager <IdentityUser> >(); var user = await userManager.FindByIdAsync(input.userAppId); if (user == null) { error = true; _output.resultConfirmation = resultConfirmation.resultBad(_ResultMessage: "USER_NOT_FOUND_ERROR", _ResultDetail: input.userAppId); // If Error } if (!error) { //***** 1. Create the token (Atomic because is same Context DB) var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey@345")); var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256); string _issuer = "https://localhost:44359"; string _audience = "https://localhost:44359"; // Get the claim from the Database and group to avoid duplicates var claimsDb = await(from claim in contextMGT.UserClaims where claim.UserId.Equals(user.Id) group claim by new { claim.ClaimType, claim.ClaimValue } into g select new Claim(g.Key.ClaimType, g.Key.ClaimValue) ).ToListAsync() ; var tokenOptions = new JwtSecurityToken( issuer: _issuer, audience: _audience, claims: claimsDb, expires: DateTime.Now.AddDays(2), signingCredentials: signinCredentials ); var tokenString = new JwtSecurityTokenHandler().WriteToken(tokenOptions); System.Diagnostics.Debug.WriteLine("Token: " + tokenString); //***** 4. Save and Commit to the Database (Atomic because is same Context DB) if (!error && autoCommit) { await contextMGT.SaveChangesAsync(); // Call it only once so do all other operations first } //***** 5. Execute Send e-mails or other events once the database has been succesfully saved //***** If this task fails, there are options -> 1. Retry multiple times 2. Save the event as Delay, 3.Rollback Database, Re //***** 6. Confirm the Result (Pass | Fail) If gets to here there are not errors then return the new data from database _output.resultConfirmation = resultConfirmation.resultGood(_ResultMessage: "TOKEN_SUCESSFULLY_CREATED"); // If OK _output.token = tokenString; // The token }// if (!error) } finally { // If the context Father is null the context was created on his own, so dispose it if (contextMGT != null && contextFather == null) { contextMGT.Dispose(); } } } catch (Exception ex) // Main try { System.Diagnostics.Debug.WriteLine("Error: " + ex.Message); string innerError = (ex.InnerException != null) ? ex.InnerException.Message : ""; System.Diagnostics.Debug.WriteLine("Error Inner: " + innerError); _output = new CreateTokenTxn_Output(); // Restart variable to avoid returning any already saved data _output.resultConfirmation = resultConfirmation.resultBad(_ResultMessage: "EXCEPTION", _ResultDetail: ex.Message); } finally { // Save Logs if needed } return(_output); }