private void ValidateEntry(TransactionState state) { Assert(state != null); Assert(state.Version >= 1); Assert(state.Status.IsValid()); Assert(state.Operations != null); Assert(state.Id != 0); var lastId = 0L; for (var i = 0; i < state.Operations.Length; i++) { var operation = state.Operations[i]; Assert(operation.OperationType.IsValid()); Assert(operation.EntryType != null); Assert(operation.ExpectedVersion != null, operation.ExpectedVersion >= 0); Assert(state.Status == TransactionStatus.Prepare, operation.State == OperationState.Unapplied); Assert(state.Status == TransactionStatus.Initial, operation.State == OperationState.Unapplied); Assert(state.Status == TransactionStatus.Committed, operation.State == OperationState.Applied); Assert(state.Status == TransactionStatus.CleanedUp, operation.State == OperationState.Applied); Assert(state.Status == TransactionStatus.Aborted, operation.State == OperationState.Unapplied); Assert(lastId < operation.Id); var predicate = DataPropertyHelper.CompilePredicate(operation.EntryType, operation.Entry); Assert(!state.Operations.Skip(i + 1).Any(p => p.EntryType == operation.EntryType && predicate(p.Entry))); lastId = operation.Id; } }
public ITransactionState AddOperation(ITransactionState state, OperationType operationType, Type entryType, object entry, int?expectedVersion, out IOperation operation) { var convertedState = ToTransactionState(state); if (convertedState.Status != TransactionStatus.Initial) { throw new InvalidOperationException("Cannot modify a this after it has started."); } if (entryType == null) { throw new ArgumentNullException(nameof(entryType)); } if (entry == null) { throw new ArgumentNullException(nameof(entry)); } #if DEBUG ValidateEntry(convertedState); #endif if (!operationType.IsValid()) { throw new ArgumentException($"The argument must be one of the values defined in { typeof(OperationType).FullName }.", nameof(operationType)); } if (expectedVersion < 0) { throw new ArgumentOutOfRangeException(nameof(expectedVersion)); } if (entryType.IsGenericTypeDefinition) { throw new ArgumentException("The argument must not be an open generic type.", nameof(entryType)); } if (!entryType.IsClass || (entryType.IsGenericType && entryType.GetGenericTypeDefinition() == typeof(Nullable <>))) { throw new ArgumentException("The argument must be a reference type.", nameof(entryType)); } if (!entryType.IsAssignableFrom(entry.GetType())) { throw new ArgumentException($"The specified entry must be of a type assignale to '{nameof(entryType)}'."); } var predicate = DataPropertyHelper.CompilePredicate(entryType, entry); if (convertedState.Operations.Any(p => p.EntryType == entryType && predicate(p.Entry))) { throw new InvalidOperationException("The this cannot have multiple operations for a single entry."); } var operationId = GetNextOperationId(convertedState); var op = new Operation(convertedState.Id, operationId, operationType, entryType, entry, expectedVersion, OperationState.Unapplied); operation = op; return(new TransactionState(convertedState.Id, convertedState.Version + 1, TransactionStatus.Initial, convertedState.Operations.Add(op))); }