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 ProjectionResult(TProjection result) { Assert(result != null); Result = result; ResultId = DataPropertyHelper.GetId <TProjectionId, TProjection>(result); }
private static void BuildIdAccess(Type entityType, ref MetadataStorage metadataStorage, out Func <object, string> idGetAccessor, out Action <object, string> idSetAccessor) { var idMember = GetIdMember(entityType); if (idMember == null) { var ms = metadataStorage = metadataStorage ?? new MetadataStorage(); string GetIdFromMetadata(object entity) { return(ms.GetMetadata(entity).GetId()); } void SetIdToMetadata(object entity, string id) { ms.GetMetadata(entity).SetId(id); } idGetAccessor = GetIdFromMetadata; idSetAccessor = SetIdToMetadata; return; } // We could theoretically allow setting id to an entity, // but this will need support for transforming a stringified id back to its original representation. idSetAccessor = null; var idType = DataPropertyHelper.GetIdType(entityType); var entityParameter = Expression.Parameter(typeof(object), "entity"); var convertedEntity = Expression.Convert(entityParameter, entityType); var idAccess = default(Expression); if (idMember.MemberType == MemberTypes.Method) { idAccess = Expression.Call(convertedEntity, (MethodInfo)idMember); } else if (idMember.MemberType == MemberTypes.Field || idMember.MemberType == MemberTypes.Property) { idAccess = Expression.MakeMemberAccess(convertedEntity, idMember); } var toStringMethod = idType.GetMethod(nameof(ToString), BindingFlags.Public | BindingFlags.Instance, Type.DefaultBinder, Type.EmptyTypes, null); Assert(toStringMethod != null); if (idAccess != null) { var toStringCall = Expression.Call(idAccess, toStringMethod); idGetAccessor = Expression.Lambda <Func <object, string> >(toStringCall, entityParameter).Compile(); } else { idGetAccessor = null; } }
public void Apply(BsonClassMap classMap) { var idMember = DataPropertyHelper.GetIdMember(classMap.ClassType); if (idMember != null) { classMap.MapIdMember(idMember); } }
public ValueTask <TEntry> GetOneAsync <TId, TEntry>(TId id, CancellationToken cancellation = default) where TId : IEquatable <TId> where TEntry : class { if (id == null) { throw new ArgumentNullException(nameof(id)); } return(GetTypedStore <TEntry>().GetOneAsync(DataPropertyHelper.BuildPredicate <TId, TEntry>(id), cancellation)); }
private static string GetId(T aggregate) { if (typeof(T).IsAssignableFrom(typeof(AggregateRootBase))) { return((aggregate as AggregateRootBase)?.Id); } if (aggregate == null) { return(null); } return(DataPropertyHelper.GetId(typeof(T), aggregate).ToString()); }
private static long GetRevision(T aggregate) { if (typeof(T).IsAssignableFrom(typeof(AggregateRootBase))) { return((aggregate as AggregateRootBase)?.Revision ?? 0); } if (aggregate == null) { return(0); } return(DataPropertyHelper.GetRevision(typeof(T), aggregate)); }
private static Expression <Func <TEntry, bool> > BuildPredicate <TEntry>(TEntry comparand, Expression <Func <TEntry, TEntry, bool> > equalityComparer) { Assert(comparand != null); Assert(equalityComparer != null); var idSelector = DataPropertyHelper.BuildPredicate(comparand); var comparandConstant = Expression.Constant(comparand, typeof(TEntry)); var parameter = equalityComparer.Parameters.First(); var equality = ParameterExpressionReplacer.ReplaceParameter(equalityComparer.Body, equalityComparer.Parameters.Last(), comparandConstant); var idEquality = ParameterExpressionReplacer.ReplaceParameter(idSelector.Body, idSelector.Parameters.First(), parameter); var body = Expression.AndAlso(idEquality, equality); return(Expression.Lambda <Func <TEntry, bool> >(body, parameter)); }
private IInMemoryDatabase <TData> CreateTypedStore <TData>() where TData : class { var dataType = typeof(TData); var idType = DataPropertyHelper.GetIdType <TData>(); if (idType == null) { throw new Exception($"Cannot store objects of type '{typeof(TData).FullName}'. An id cannot be extracted."); // TODO } var typedStore = Activator.CreateInstance(typeof(InMemoryDatabase <,>).MakeGenericType(idType, dataType)); return((IInMemoryDatabase <TData>)typedStore); }
public static async ValueTask <IEntryState <TId, TData> > GetEntryAsync <TId, TData>(this IEntryStateStorage <TId, TData> entryStorage, IEntryState <TId, TData> comparand, CancellationToken cancellation = default) where TData : class { if (entryStorage == null) { throw new ArgumentNullException(nameof(entryStorage)); } if (comparand == null) { throw new ArgumentNullException(nameof(comparand)); } var entries = await entryStorage.GetEntriesAsync(DataPropertyHelper.BuildPredicate(comparand), cancellation); return(entries.FirstOrDefault()); }
private async Task <bool> CheckComparandToBeUpToDate <TEntry>(TEntry comparand, Expression <Func <TEntry, TEntry, bool> > equalityComparer, CancellationToken cancellation) where TEntry : class { var result = await GetOneAsync(DataPropertyHelper.BuildPredicate(comparand), cancellation); if (comparand == null) { return(result == null); } if (result == null) { return(false); } return(equalityComparer.Compile(preferInterpretation: true).Invoke(comparand, result)); }
public async ValueTask <TEntry> GetOrAdd <TEntry>(TEntry entry, CancellationToken cancellation = default) where TEntry : class { if (entry == null) { throw new ArgumentNullException(nameof(entry)); } while (!await AddAsync(entry, cancellation)) { var result = await GetOneAsync(DataPropertyHelper.BuildPredicate(entry), cancellation); if (result != null) { return(result); } } return(entry); }
public async Task RemoveEntityFromProjectionAsync(ProjectionSourceDescriptor projectionSource, ProjectionTargetDescriptor removedProjection, CancellationToken cancellation) { var(originalMetadata, metadata) = await GetMetadataAsync(removedProjection, cancellation); if (metadata == null) { Assert(false); return; } var removed = metadata.ProjectionSources .RemoveFirstWhere(p => p.Id == projectionSource.SourceId && p.Type == projectionSource.SourceType.GetUnqualifiedTypeName()); Assert(removed != null); if (!metadata.ProjectionSources.Any()) { _targetMetadataCache[removedProjection] = new ProjectionTargetMetadataCacheEntry(originalMetadata, metadata: null, touched: true); var predicate = DataPropertyHelper.BuildPredicate <TProjectionId, TProjection>(metadata.TargetId); var projection = await _database.GetAsync(predicate, cancellation).FirstOrDefault(); if (projection != null) { _targetsToDelete.Add(projection); //await _database.RemoveAsync(projection, cancellation); } } _targetMetadataCache[removedProjection] = new ProjectionTargetMetadataCacheEntry(originalMetadata, metadata, touched: true); }
private static MemberInfo GetIdMember(Type entityType) { return(DataPropertyHelper.GetIdMember(entityType)); }
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))); }