async Task P_SaveAsync(TEntity entity, SaveOpCode opCode, bool prohibitReferenceKeyAssignment, IContext ctx = default) { if (entity is null) { throw new ArgumentNullException(paramName: nameof(entity)); } // await P_SaveAsync(entityCollection : new TEntity[] { entity }, opCode : opCode, prohibitReferenceKeyAssignment : prohibitReferenceKeyAssignment, ctx : ctx).ConfigureAwait(false); }
// TODO: Put strings into the resources. // async Task P_SaveAsync(TEntity[] entityCollection, SaveOpCode opCode, bool prohibitReferenceKeyAssignment, IContext ctx = default) { if (entityCollection is null) { throw new ArgumentNullException(paramName: nameof(entityCollection)); } // var entityCollectionLength = entityCollection.Length; if (entityCollectionLength > 0) { var entityCollectionLowerBound = entityCollection.GetLowerBound(dimension: 0); var locEntityCollection = new P_EntityChangeTrackingStateWrap <TEntity> [entityCollectionLength]; var ct = ctx.Ct(); var efContext = GetDataChangeEfDbContext(); switch (opCode) { case SaveOpCode.Write: if (prohibitReferenceKeyAssignment) { for (var offset = 0; offset < entityCollectionLength; offset++) { var entity = entityCollection[offset + entityCollectionLowerBound]; if (entity is null) { throw new ArgumentNullException(paramName: $"{nameof(entityCollection)}[{offset + entityCollectionLowerBound:d}]"); } else { locEntityCollection[offset].Entity = entity; locEntityCollection[offset].State = entity.IsNew ? EntityState.Added : EntityState.Modified; } } } else { IReferenceKeyProvider <TReferenceKey> keyProvider = default; for (var offset = 0; offset < entityCollectionLength; offset++) { var entity = entityCollection[offset + entityCollectionLowerBound]; if (entity is null) { throw new ArgumentNullException(paramName: $"{nameof(entityCollection)}[{offset + entityCollectionLowerBound:d}]"); } else { locEntityCollection[offset].Entity = entity; if (entity.IsNew) { keyProvider = keyProvider ?? await P_RequireReferenceKeyProviderAsync(ctx : ctx).ConfigureAwait(false); entity.ReferenceKey = await keyProvider.NextKeyAsync(ctx : ctx).ConfigureAwait(false); locEntityCollection[offset].State = EntityState.Added; } else { locEntityCollection[offset].State = EntityState.Modified; } } } } break; case SaveOpCode.Insert: if (prohibitReferenceKeyAssignment) { for (var offset = 0; offset < entityCollectionLength; offset++) { var entity = entityCollection[offset + entityCollectionLowerBound]; if (entity is null) { throw new ArgumentNullException(paramName: $"{nameof(entityCollection)}[{offset + entityCollectionLowerBound:d}]"); } else { locEntityCollection[offset].Entity = entity; locEntityCollection[offset].State = EntityState.Added; } } } else { IReferenceKeyProvider <TReferenceKey> keyProvider = default; for (var offset = 0; offset < entityCollectionLength; offset++) { var entity = entityCollection[offset + entityCollectionLowerBound]; if (entity is null) { throw new ArgumentNullException(paramName: $"{nameof(entityCollection)}[{offset + entityCollectionLowerBound:d}]"); } else { if (entity.IsNew) { keyProvider = keyProvider ?? await P_RequireReferenceKeyProviderAsync(ctx : ctx).ConfigureAwait(false); entity.ReferenceKey = await keyProvider.NextKeyAsync(ctx : ctx).ConfigureAwait(false); } locEntityCollection[offset].Entity = entity; locEntityCollection[offset].State = EntityState.Added; } } } break; case SaveOpCode.Update: for (var offset = 0; offset < entityCollectionLength; offset++) { var entity = entityCollection[offset + entityCollectionLowerBound]; if (entity is null) { throw new ArgumentNullException(paramName: $"{nameof(entityCollection)}[{offset + entityCollectionLowerBound:d}]"); } else { locEntityCollection[offset].Entity = entity; locEntityCollection[offset].State = EntityState.Modified; } } break; case SaveOpCode.Delete: for (var offset = 0; offset < entityCollectionLength; offset++) { var entity = entityCollection[offset + entityCollectionLowerBound]; if (entity is null) { throw new ArgumentNullException(paramName: $"{nameof(entityCollection)}[{offset + entityCollectionLowerBound:d}]"); } else { locEntityCollection[offset].Entity = entity; locEntityCollection[offset].State = EntityState.Deleted; } } break; default: throw new NotSupportedException(message: $"Store operation code '{opCode}' is not supported."); } // Сохранение. // var saveFault = default(Exception); try { for (var y = 0; y < entityCollectionLength; y++) { try { (locEntityCollection[y].Entry = efContext.Entry(entity: locEntityCollection[y].Entity)).State = locEntityCollection[y].State; } catch (Exception exception) { throw new EonException( message: $"An exception occurred while placing the entity in EF context (change tracker).{Environment.NewLine}\tEntity ({y + 1:d} from {entityCollectionLength:d}):{locEntityCollection[ y ].Entity.FmtStr().GNLI2()}{Environment.NewLine}\tPlacing entity state:{locEntityCollection[ y ].State.FmtStr().GNLI2()}{Environment.NewLine}\tStore operation code:{opCode.FmtStr().GNLI2()}", innerException: exception); } } // try { await efContext.SaveChangesAsync(acceptAllChangesOnSuccess : true, ct : ct).ConfigureAwait(false); } catch (Exception exception) { #if TRG_NETFRAMEWORK if (exception is OptimisticConcurrencyException || exception is DbUpdateConcurrencyException) #else if (exception is DbUpdateConcurrencyException) #endif { throw new DataStorageOptimisticConcurrencyException(message: $"Объект:{(locEntityCollection.Length == 1 ? locEntityCollection[ 0 ].Entity?.ToString() : $"<коллекция из {locEntityCollection.Length:d} элементов>").FmtStr().GNLI2()}"); } throw; } } catch (Exception exception) { saveFault = exception; throw; } finally { try { for (var y = 0; y < entityCollectionLength; y++) { locEntityCollection[y].Entry.State = EntityState.Detached; } } catch (Exception exception) { if (saveFault is null) { throw; } else { throw new AggregateException(saveFault, exception); } } } } }