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;
            }
        }
Example #2
0
            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;
                }
            }
Example #4
0
            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));
        }
Example #6
0
        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());
        }
Example #7
0
        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());
        }
Example #11
0
        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));
        }
Example #12
0
        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);
        }
Example #13
0
            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)));
        }