public static ValueTask <int> RelationalUpdateAsync <T>(this DbContext context, T entity) where T : class { var configuration = new RelationalUpdateConfiguration { UpdatedTypes = context.Entry(entity).GetCollectionTypes() }; return(RelationalUpdateAsync(context, entity, configuration)); }
public static async ValueTask <int> RelationalUpdateAsync <T>(this DbContext context, T entity, RelationalUpdateConfiguration configuration) where T : class { var entry = context.Entry(entity); var primaryKey = entry.GetPrimaryKeyValue(); var navigation = entry.Metadata.GetNavigations().ToList(); foreach (var collectionType in configuration.UpdatedTypes) { var propertyName = navigation.Where(p => p.ClrType.GetFirstGenericArgument() == collectionType.Type).Select(p => p.Name).FirstOrDefault(); if (string.IsNullOrEmpty(propertyName)) { continue; } var collection = entry.Collection(propertyName); var foreignKey = collection.Metadata.ForeignKey; var primaryKeyName = collection.EntityEntry.GetPrimaryKeyName(); var collectionValues = (IEnumerable <dynamic>)entity.GetType().GetProperty(propertyName)?.GetValue(entity, null); var dynamicList = (collectionValues ?? throw new InvalidOperationException()).ToDynamicList(); var currentIds = dynamicList.Select(p => p.GetType().GetProperty(primaryKeyName)?.GetValue(p, null)) .ToList(); var fkName = foreignKey.Properties.FirstOrDefault()?.Name; var databaseValues = await context.Query(collectionType.Type) .AsQueryable() .Where($"{fkName} == @0", primaryKey) .ToDynamicListAsync(); var databaseIds = databaseValues .Select(p => p.GetType().GetProperty(primaryKeyName)?.GetValue(p, null)) .ToList(); if (collectionType.RemoveDataInDatabase) { var deletedItems = databaseValues.Where(p => currentIds.Contains(p.GetType().GetProperty(primaryKeyName)?.GetValue(p, null)) == false) .ToList(); context.RemoveRange(deletedItems); } foreach (dynamic o in dynamicList) { var id = o.GetType().GetProperty(primaryKeyName)?.GetValue(o, null); if (databaseIds.Contains(id)) { context.Entry(o).State = EntityState.Modified; } else { o.GetType().GetProperty(fkName)?.SetValue(o, primaryKey, null); context.Entry(o).State = EntityState.Added; } } } if (configuration.TriggerSaveChanges) { return(await context.SaveChangesAsync()); } return(0); }