protected override async Task <List <int> > SaveExecuteAsync(List <AgentForSave> entities, ExpandExpression expand, bool returnIds) { var(blobsToDelete, blobsToSave, imageIds) = await ImageUtilities.ExtractImages <Agent, AgentForSave>(_repo, entities, BlobName); // Save the agents var ids = await _repo.Agents__Save( DefinitionId, entities : entities, imageIds : imageIds, returnIds : returnIds); // Delete the blobs retrieved earlier if (blobsToDelete.Any()) { await _blobService.DeleteBlobsAsync(blobsToDelete); } // Save new blobs if any if (blobsToSave.Any()) { await _blobService.SaveBlobsAsync(blobsToSave); } return(ids); }
public async Task DeleteBlobsAsync(IEnumerable <string> blobNames) { await _blobService.DeleteBlobsAsync(blobNames); }
protected override async Task <List <int> > SaveExecuteAsync(List <DocumentForSave> entities, ExpandExpression expand, bool returnIds) { var blobsToSave = new List <(string, byte[])>(); // Prepare the list of attachments with extras var attachments = new List <AttachmentWithExtras>(); foreach (var(doc, docIndex) in entities.Select((d, i) => (d, i))) { if (doc.Attachments != null) { doc.Attachments.ForEach(att => { var attWithExtras = new AttachmentWithExtras { Id = att.Id, FileName = att.FileName, FileExtension = att.FileExtension, DocumentIndex = docIndex, }; // If new attachment if (att.Id == 0) { // Add extras: file Id and size byte[] file = att.File; string fileId = Guid.NewGuid().ToString(); attWithExtras.FileId = fileId; attWithExtras.Size = file.LongLength; // Also add to blobsToCreate string blobName = BlobName(fileId); blobsToSave.Add((blobName, file)); } attachments.Add(attWithExtras); }); } } // Save the documents var(ids, fileIdsToDelete) = await _repo.Documents__Save( DefinitionId, documents : entities, attachments : attachments, returnIds : returnIds); // Assign new documents to the current user var userInfo = await _repo.GetUserInfoAsync(); var currentUserId = userInfo.UserId.Value; var newDocIds = entities.Select((doc, index) => (doc, index)).Where(e => e.doc.Id == 0).Select(e => ids[e.index]); await _repo.Documents__Assign(newDocIds, currentUserId, null); // Delete the file Ids retrieved earlier if any if (fileIdsToDelete.Any()) { var blobsToDelete = fileIdsToDelete.Select(fileId => BlobName(fileId)); await _blobService.DeleteBlobsAsync(blobsToDelete); } // Save new blobs if any if (blobsToSave.Any()) { await _blobService.SaveBlobsAsync(blobsToSave); } // Return the new Ids return(ids); }
protected override async Task <List <int> > SaveExecuteAsync(List <UserForSave> entities, ExpandExpression expand, bool returnIds) { // NOTE: this method is not optimized for massive bulk (e.g. 1,000+ users), since it relies // on querying identity through UserManager one email at a time but it should be acceptable // with the usual workloads, customers with more than 200 users are rare anyways // Step (1) enlist the app repo _appRepo.EnlistTransaction(Transaction.Current); // So that it is not affected by admin trx scope later // Step (2): If Embedded Identity Server is enabled, create any emails that don't already exist there var usersToInvite = new List <(EmbeddedIdentityServerUser IdUser, UserForSave User)>(); if (_options.EmbeddedIdentityServerEnabled) { _identityTrxScope = ControllerUtilities.CreateTransaction(TransactionScopeOption.RequiresNew); foreach (var entity in entities) { var email = entity.Email; // In case the user was added in a previous failed transaction // or something, we always try to be forgiving in the code var identityUser = await _userManager.FindByNameAsync(email) ?? await _userManager.FindByEmailAsync(email); // This is truly a new user, create it if (identityUser == null) { // Create the identity user identityUser = new EmbeddedIdentityServerUser { UserName = email, Email = email, EmailConfirmed = !_options.EmailEnabled // Note: If the system is integrated with an email service, user emails // are automatically confirmed, otherwise users must confirm their }; var result = await _userManager.CreateAsync(identityUser); if (!result.Succeeded) { string msg = string.Join(", ", result.Errors.Select(e => e.Description)); _logger.LogError(msg); throw new BadRequestException($"An unexpected error occurred while creating an account for '{email}'"); } } // Mark for invitation later if (!identityUser.EmailConfirmed) { usersToInvite.Add((identityUser, entity)); } } } // Step (3): Extract the images var(blobsToDelete, blobsToSave, imageIds) = await ImageUtilities.ExtractImages <User, UserForSave>(_appRepo, entities, BlobName); // Step (4): Save the users in the app database var ids = await _appRepo.Users__Save(entities, imageIds, returnIds); // Step (5): Delete old images from the blob storage if (blobsToDelete.Any()) { await _blobService.DeleteBlobsAsync(blobsToDelete); } // Step (6): Save new images to the blob storage if (blobsToSave.Any()) { await _blobService.SaveBlobsAsync(blobsToSave); } // Step (7) Same the emails in the admin database var tenantId = _tenantIdAccessor.GetTenantId(); _adminTrxScope = ControllerUtilities.CreateTransaction(TransactionScopeOption.RequiresNew); _adminRepo.EnlistTransaction(Transaction.Current); var oldEmails = new List <string>(); // Emails are readonly after the first save var newEmails = entities.Where(e => e.Id == 0).Select(e => e.Email); await _adminRepo.GlobalUsers__Save(newEmails, oldEmails, tenantId); // Step (8): Send the invitation emails if (usersToInvite.Any()) // This will be empty if embedded identity is disabled or if email is disabled { var userIds = usersToInvite.Select(e => e.User.Id).ToArray(); var tos = new List <string>(); var subjects = new List <string>(); var substitutions = new List <Dictionary <string, string> >(); foreach (var(idUser, user) in usersToInvite) { // Add the email sender parameters var(subject, body) = await MakeInvitationEmailAsync(idUser, user.Name, user.Name2, user.Name3, user.PreferredLanguage); tos.Add(idUser.Email); subjects.Add(subject); substitutions.Add(new Dictionary <string, string> { { "-message-", body } }); } await _emailSender.SendEmailBulkAsync( tos : tos, subjects : subjects, htmlMessage : $"-message-", substitutions : substitutions.ToList() ); } // Return the new Ids return(ids); }