/// <inheritdoc /> public async Task <ICollection <User> > CreateUser <TModel, TRegisterModel>( ICollection <TRegisterModel> models, UpdateOptions options = null, CancellationToken cancellation = default) where TModel : User, IOwnerAbstractModel, new() where TRegisterModel : IRegistrationModel <TModel> { await _identityService.RetrieveUserAsync(); var dbModels = models.Select(m => m.ToModel()).ToList(); var dbSet = _dbContext.Set <TModel>(); var roles = models.SelectMany(m => m.Groups).ToList(); var dbRoles = await _dbContext.Roles.Where(r => roles.Contains(r.Name)).ToListAsync(cancellation); using (var transaction = _dbContext.Database.BeginTransaction()) { try { await MergeReferences(dbModels, options, cancellation); var modelPairs = models.Select(model => (model, model.ToModel())).ToList(); // Create each user var createdUsers = new List <User>(); foreach (var(registrationModel, model) in modelPairs) { if (model.Id == Guid.Empty) { model.Id = Guid.NewGuid(); } foreach (var group in registrationModel.Groups) { var role = dbRoles.First(r => r.Name == group); _dbContext.UserRoles.Add(new IdentityUserRole <Guid> { UserId = model.Id, RoleId = role.Id }); } // Validate the password matches the applications password strength var validationTasks = _userManager .PasswordValidators .Select(v => v.ValidateAsync(_userManager, model, registrationModel.Password)); var validationResults = await Task.WhenAll(validationTasks); var failed = validationResults.Where(r => r.Succeeded == false).ToList(); if (failed.Any()) { throw new AggregateException(failed .SelectMany(f => f.Errors.Select(s => s.Description)) .Select(s => new InvalidOperationException(s))); } model.UserName = model.Email; model.PasswordHash = _userManager.PasswordHasher.HashPassword(model, registrationModel.Password); model.ConcurrencyStamp = await _userManager.GenerateConcurrencyStampAsync(model); model.NormalizedEmail = _userManager.NormalizeEmail(model.Email); model.NormalizedUserName = _userManager.NormalizeName(model.UserName); model.EmailConfirmed = true; model.SecurityStamp = Guid.NewGuid().ToString(); dbSet.Update(model); createdUsers.Add(model); } // Ensure that we create all of the base entities instead of updating var addedEntries = _dbContext.ChangeTracker.Entries() .Where(entry => createdUsers.Contains(entry.Entity)); foreach (var entry in addedEntries) { entry.State = EntityState.Added; } var fileModels = _dbContext .ChangeTracker .Entries() .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified) .Select(e => e.Entity) .ToList(); if (options?.Files != null) { await SaveFiles(fileModels, options.Files, cancellation); } else { ClearFileAttributes(models); } await AssignModelMetaData(_identityService.User, cancellation); ValidateModels(_dbContext.ChangeTracker .Entries() .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified) .Select(e => e.Entity)); foreach (var user in createdUsers) { user.Owner = user.Id; } var errors = await _securityService.CheckDbSecurityChanges(_identityService, _dbContext); if (errors.Any()) { throw new AggregateException( errors.Select(error => new InvalidOperationException(error))); } foreach (var entry in _dbContext.ChangeTracker.Entries <IAbstractModel>().ToList()) { await entry.Entity.BeforeSave(entry.State, _dbContext, _serviceProvider); } var changes = _dbContext .ChangeTracker .Entries <IAbstractModel>() .Select(e => new ChangeState { State = e.State, Entry = e }) .ToList(); await _dbContext.SaveChangesAsync(cancellation); foreach (var change in changes) { await change.Entry.Entity.AfterSave(change.State, _dbContext, _serviceProvider, changes); } transaction.Commit(); return(createdUsers); } catch (Exception e) { _logger.LogError("Error completing create user action - {Error}", e.ToString()); throw; } } }
/// <inheritdoc /> public async Task <ICollection <T> > Update <T>( ICollection <T> models, UpdateOptions options = null, CancellationToken cancellation = default) where T : class, IOwnerAbstractModel, new() { await _identityService.RetrieveUserAsync(); var dbSet = _dbContext.Set <T>(); using (var transaction = _dbContext.Database.BeginTransaction()) { try { await MergeReferences(models, options, cancellation); foreach (var model in models) { dbSet.Update(model); } var fileModels = _dbContext .ChangeTracker .Entries() .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified) .Select(e => e.Entity) .ToList(); if (options?.Files != null) { await SaveFiles(fileModels, options.Files, cancellation); } else { ClearFileAttributes(models); } await AssignModelMetaData(_identityService.User, cancellation); ValidateModels(_dbContext.ChangeTracker .Entries() .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified) .Select(e => e.Entity)); var errors = await _securityService.CheckDbSecurityChanges(_identityService, _dbContext); if (errors.Any()) { throw new AggregateException( errors.Select(error => new InvalidOperationException(error))); } foreach (var entry in _dbContext.ChangeTracker.Entries <IAbstractModel>().ToList()) { await entry.Entity.BeforeSave(entry.State, _dbContext, _serviceProvider); } var changes = _dbContext .ChangeTracker .Entries <IAbstractModel>() .Select(e => new ChangeState { State = e.State, Entry = e }) .ToList(); await _dbContext.SaveChangesAsync(cancellation); foreach (var change in changes) { await change.Entry.Entity.AfterSave(change.State, _dbContext, _serviceProvider, changes); } transaction.Commit(); return(models); } catch (Exception e) { _logger.LogError("Error completing update action - {Error}", e.ToString()); throw; } } }