Example #1
0
        async Task IFilter <ConsumeContext <TMessage> > .Send(ConsumeContext <TMessage> context, IPipe <ConsumeContext <TMessage> > next)
        {
            Stopwatch timer = Stopwatch.StartNew();

            try
            {
                ISagaQuery <TSaga> query = _queryFactory.CreateQuery(context);

                SagaQueryConsumeContext <TSaga, TMessage> queryContext = new SagaQueryConsumeContextProxy <TSaga, TMessage>(context, query);

                await Task.Yield();

                await _sagaRepository.SendQuery(queryContext, _policy, _messagePipe).ConfigureAwait(false);

                await next.Send(context).ConfigureAwait(false);

                await context.NotifyConsumed(timer.Elapsed, TypeMetadataCache <TSaga> .ShortName).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                await context.NotifyFaulted(timer.Elapsed, TypeMetadataCache <TSaga> .ShortName, ex).ConfigureAwait(false);

                throw;
            }
        }
        public async Task SendQuery <T>(ConsumeContext <T> context, ISagaQuery <TSaga> query, IPipe <SagaRepositoryQueryContext <TSaga, T> > next)
            where T : class
        {
            var dbContext = _dbContextFactory.CreateScoped(context);

            try
            {
                var lockContext = await _lockStrategy.CreateLockContext(dbContext, query, context.CancellationToken).ConfigureAwait(false);

                var repositoryContext = new DbContextSagaRepositoryContext <TSaga, T>(dbContext, context, _consumeContextFactory, _lockStrategy);

                await WithinTransaction(dbContext, async() =>
                {
                    var instances = await lockContext.Load().ConfigureAwait(false);

                    var queryContext = new LoadedSagaRepositoryQueryContext <TSaga, T>(repositoryContext, instances);

                    await next.Send(queryContext).ConfigureAwait(false);
                }).ConfigureAwait(false);
            }
            finally
            {
                _dbContextFactory.Release(dbContext);
            }
        }
Example #3
0
 public async Task <IEnumerable <Guid> > Find(ISagaQuery <TSaga> query)
 {
     return(await _collection.Find(query.FilterExpression)
            .Project(x => x.CorrelationId)
            .ToListAsync()
            .ConfigureAwait(false));
 }
Example #4
0
 public Task SendQuery <T>(
     ConsumeContext <T> context, ISagaQuery <TSaga> query, ISagaPolicy <TSaga, T> policy,
     IPipe <SagaConsumeContext <TSaga, T> > next
     ) where T : class
 {
     throw new NotImplementedByDesignException("EventStore saga repository does not support queries");
 }
Example #5
0
        public async Task SendQuery <T>(ConsumeContext <T> context, ISagaQuery <TSaga> query, ISagaPolicy <TSaga, T> policy,
                                        IPipe <SagaConsumeContext <TSaga, T> > next)
            where T : class
        {
            try
            {
                await _repositoryContextFactory.SendQuery(context, query, new SendQuerySagaPipe <TSaga, T>(policy, next)).ConfigureAwait(false);
            }
            catch (SagaException exception)
            {
                context.LogFault(this, exception);
                throw;
            }
            catch (DataException exception)
            {
                context.LogFault(this, exception);
                throw;
            }
            catch (Exception exception)
            {
                context.LogFault(this, exception);

                throw new SagaException(exception.Message, typeof(TSaga), typeof(T), Guid.Empty, exception);
            }
        }
        public static async Task <Guid?> ShouldContainSagaInState <TStateMachine, TInstance>(this ISagaRepository <TInstance> repository,
                                                                                             Expression <Func <TInstance, bool> > expression, TStateMachine machine, State state, TimeSpan timeout)
            where TStateMachine : SagaStateMachine <TInstance>
            where TInstance : class, SagaStateMachineInstance
        {
            var querySagaRepository = repository as IQuerySagaRepository <TInstance>;

            if (querySagaRepository == null)
            {
                throw new ArgumentException("The repository must support querying", nameof(repository));
            }

            var giveUpAt = DateTime.Now + timeout;

            ISagaQuery <TInstance> query = machine.CreateSagaQuery(expression, state);

            while (DateTime.Now < giveUpAt)
            {
                var saga = (await querySagaRepository.Find(query).ConfigureAwait(false)).FirstOrDefault();
                if (saga != Guid.Empty)
                {
                    return(saga);
                }

                await Task.Delay(10).ConfigureAwait(false);
            }

            return(default);
 public OptimisticSagaLockContext(DbContext context, ISagaQuery <TSaga> query, CancellationToken cancellationToken, ILoadQueryProvider <TSaga> provider)
 {
     _context           = context;
     _query             = query;
     _cancellationToken = cancellationToken;
     _provider          = provider;
 }
Example #8
0
        public async Task <SagaRepositoryQueryContext <TSaga> > Query(ISagaQuery <TSaga> query, CancellationToken cancellationToken = default)
        {
            IReadOnlyList <TSaga> instances = await _session.Query <TSaga>()
                                              .Where(query.FilterExpression)
                                              .ToListAsync(cancellationToken).ConfigureAwait(false);

            return(new LoadedSagaRepositoryQueryContext <TSaga>(this, instances));
        }
        Task ISagaRepository <TSaga> .SendQuery <T>(ConsumeContext <T> context, ISagaQuery <TSaga> query, ISagaPolicy <TSaga, T> policy,
                                                    IPipe <SagaConsumeContext <TSaga, T> > next)
        {
            var interceptPipe   = new InterceptPipe <T>(_sagas, _received, next);
            var interceptPolicy = new InterceptPolicy <T>(_created, policy);

            return(_sagaRepository.SendQuery(context, query, interceptPolicy, interceptPipe));
        }
        public async Task <IEnumerable <Guid> > Find(ISagaQuery <TSaga> query)
        {
            IEnumerable <TSaga> sagas = await _client.CreateDocumentQuery <TSaga>(UriFactory.CreateDocumentCollectionUri(_databaseName, _collectionName), _feedOptions)
                                        .Where(query.FilterExpression)
                                        .QueryAsync()
                                        .ConfigureAwait(false);

            return(sagas.Select(x => x.CorrelationId));
        }
        public async Task <IEnumerable <Guid> > Find(ISagaQuery <TSaga> query)
        {
            // This will not work for Document Db because the .Where needs to look for [JsonProperty("id")], and if you pass in CorrelationId property, the ISaga doesn't have that property.
            IEnumerable <TSaga> sagas = await _client.CreateDocumentQuery <TSaga>(UriFactory.CreateDocumentCollectionUri(_databaseName, _collectionName), _feedOptions)
                                        .Where(query.FilterExpression)
                                        .QueryAsync()
                                        .ConfigureAwait(false);

            return(sagas.Select(x => x.CorrelationId));
        }
Example #12
0
 public async Task <IEnumerable <Guid> > Find(ISagaQuery <TSaga> query)
 {
     using (var session = _sessionFactory.OpenSession())
     {
         return(await session.QueryOver <TSaga>()
                .Where(query.FilterExpression)
                .Select(x => x.CorrelationId)
                .ListAsync <Guid>().ConfigureAwait(false));
     }
 }
 async Task <IEnumerable <Guid> > IQuerySagaRepository <TSaga> .Find(ISagaQuery <TSaga> query)
 {
     using (var dbContext = _sagaDbContextFactory())
     {
         return(await dbContext.Set <TSaga>()
                .Where(query.FilterExpression)
                .Select(x => x.CorrelationId)
                .ToListAsync().ConfigureAwait(false));
     }
 }
Example #14
0
 public async Task <IEnumerable <Guid> > Find(ISagaQuery <TSaga> query)
 {
     using (var session = _store.QuerySession())
     {
         return(await session.Query <TSaga>()
                .Where(query.FilterExpression)
                .Select(x => x.CorrelationId)
                .ToListAsync());
     }
 }
Example #15
0
 public async Task <IEnumerable <Guid> > Find(ISagaQuery <TSaga> query)
 {
     using (var session = OpenSession())
     {
         return(await session.Query <TSaga>()
                .Where(query.FilterExpression)
                .Customize(x => x.WaitForNonStaleResultsAsOf(DateTime.Now + TimeSpan.FromMinutes(1)))
                .Select(x => x.CorrelationId)
                .ToListAsync());
     }
 }
        async Task <IEnumerable <Guid> > IQuerySagaRepository <TSaga> .Find(ISagaQuery <TSaga> query)
        {
            using (var connection = new SqlConnection(_connectionString))
            {
                var tableName = GetTableName <TSaga>();
                var(whereStatement, parameters) = WhereStatementHelper.GetWhereStatementAndParametersFromExpression(query.FilterExpression);

                return
                    ((await connection.QueryAsync <Guid>($"SELECT CorrelationId FROM {tableName} WITH (UPDLOCK, ROWLOCK) {whereStatement}",
                                                         parameters).ConfigureAwait(false)).ToList());
            }
        }
        public async Task SendQuery <T>(ConsumeContext <T> context, ISagaQuery <TSaga> query, IPipe <SagaRepositoryQueryContext <TSaga, T> > next)
            where T : class
        {
            var repositoryContext = new MongoDbSagaRepositoryContext <TSaga, T>(_mongoCollection, context, _factory);

            IList <TSaga> instances = await _mongoCollection.Find(query.FilterExpression)
                                      .ToListAsync(repositoryContext.CancellationToken)
                                      .ConfigureAwait(false);

            var queryContext = new LoadedSagaRepositoryQueryContext <TSaga, T>(repositoryContext, instances);

            await next.Send(queryContext).ConfigureAwait(false);
        }
Example #18
0
        public async Task SendQuery <T>(ConsumeContext <T> context, ISagaQuery <TSaga> query, IPipe <SagaRepositoryQueryContext <TSaga, T> > next)
            where T : class
        {
            await _sagas.MarkInUse(context.CancellationToken).ConfigureAwait(false);

            using var repositoryContext = new InMemorySagaRepositoryContext <TSaga, T>(_sagas, _factory, context);

            var matchingInstances = _sagas.Where(query).Select(x => x.Instance.CorrelationId).ToList();

            var queryContext = new DefaultSagaRepositoryQueryContext <TSaga, T>(repositoryContext, matchingInstances);

            await next.Send(queryContext).ConfigureAwait(false);
        }
        public async Task SendQuery <T>(ConsumeContext <T> context, ISagaQuery <TSaga> query, IPipe <SagaRepositoryQueryContext <TSaga, T> > next)
            where T : class
        {
            IEnumerable <TSaga> sagas = await _context.Container.GetItemLinqQueryable <TSaga>()
                                        .Where(query.FilterExpression)
                                        .QueryAsync(context.CancellationToken)
                                        .ConfigureAwait(false);

            var repositoryContext = new CosmosSagaRepositoryContext <TSaga, T>(_context, context, _factory);

            var queryContext = new DefaultSagaRepositoryQueryContext <TSaga, T>(repositoryContext, sagas.Select(x => x.CorrelationId).ToList());

            await next.Send(queryContext).ConfigureAwait(false);
        }
Example #20
0
        public async Task SendQuery <T>(ConsumeContext <T> context, ISagaQuery <TSaga> query, IPipe <SagaRepositoryQueryContext <TSaga, T> > next)
            where T : class
        {
            using DapperDatabaseContext <TSaga> databaseContext = await CreateDatabaseContext(context.CancellationToken).ConfigureAwait(false);

            IEnumerable <TSaga> instances = await databaseContext.QueryAsync(query.FilterExpression, context.CancellationToken).ConfigureAwait(false);

            var repositoryContext = new DapperSagaRepositoryContext <TSaga, T>(databaseContext, context, _factory);

            var queryContext = new LoadedSagaRepositoryQueryContext <TSaga, T>(repositoryContext, instances);

            await next.Send(queryContext).ConfigureAwait(false);

            databaseContext.Commit();
        }
        public Task <IEnumerable <Guid> > Find(ISagaQuery <TSaga> query)
        {
            using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew))
                using (var session = _sessionFactory.OpenSession())
                {
                    IList <Guid> result = session.QueryOver <TSaga>()
                                          .Where(query.FilterExpression)
                                          .Select(x => x.CorrelationId)
                                          .List <Guid>();

                    scope.Complete();

                    return(Task.FromResult <IEnumerable <Guid> >(result));
                }
        }
        public IEnumerable <SagaInstance <TSaga> > Where(ISagaQuery <TSaga> query)
        {
            lock (_lock)
            {
                IIndexedSagaProperty <TSaga> index = HasIndexFor(query.FilterExpression);
                if (index == null)
                {
                    return(_indexById.Where(query.GetFilter()).ToList());
                }

                var rightValue = GetRightValue(query.FilterExpression);

                return(index.Where(rightValue, query.GetFilter()).ToList());
            }
        }
        public async Task SendQuery <T>(ConsumeContext <T> context, ISagaQuery <TSaga> query, IPipe <SagaRepositoryQueryContext <TSaga, T> > next)
            where T : class
        {
            // This will not work for Document Db because the .Where needs to look for [JsonProperty("id")],
            // and if you pass in CorrelationId property, the ISaga doesn't have that property. Can we .Select() it out?
            IEnumerable <TSaga> sagas = await _context.Client.CreateDocumentQuery <TSaga>(_context.Collection, _context.FeedOptions)
                                        .Where(query.FilterExpression)
                                        .QueryAsync(context.CancellationToken)
                                        .ConfigureAwait(false);

            var repositoryContext = new DocumentDbSagaRepositoryContext <TSaga, T>(_context, context, _factory);

            var queryContext = new LoadedSagaRepositoryQueryContext <TSaga, T>(repositoryContext, sagas.ToList());

            await next.Send(queryContext).ConfigureAwait(false);
        }
Example #24
0
        public async Task SendQuery <T>(ConsumeContext <T> context, ISagaQuery <TSaga> query, IPipe <SagaRepositoryQueryContext <TSaga, T> > next)
            where T : class
        {
            using var session = _documentStore.DirtyTrackedSession();

            var repositoryContext = new MartenSagaRepositoryContext <TSaga, T>(session, context, _factory);

            IReadOnlyList <TSaga> instances = await session.Query <TSaga>()
                                              .Where(query.FilterExpression)
                                              .ToListAsync().ConfigureAwait(false);


            var queryContext = new LoadedSagaRepositoryQueryContext <TSaga, T>(repositoryContext, instances.ToList());

            await next.Send(queryContext).ConfigureAwait(false);
        }
Example #25
0
        public async Task SendQuery <T>(ConsumeContext <T> context, ISagaQuery <TSaga> query, IPipe <SagaRepositoryQueryContext <TSaga, T> > next)
            where T : class
        {
            var session = _sessionFactory.OpenSession();

            ITransaction transaction = null;

            try
            {
                transaction = session.BeginTransaction();

                var repositoryContext = new NHibernateSagaRepositoryContext <TSaga, T>(session, context, _factory);

                IList <TSaga> instances = await session.QueryOver <TSaga>()
                                          .Where(query.FilterExpression)
                                          .ListAsync(repositoryContext.CancellationToken)
                                          .ConfigureAwait(false);

                var queryContext = new LoadedSagaRepositoryQueryContext <TSaga, T>(repositoryContext, instances);

                await next.Send(queryContext).ConfigureAwait(false);

                await transaction.CommitAsync(repositoryContext.CancellationToken).ConfigureAwait(false);
            }
            catch (Exception)
            {
                if (transaction != null && transaction.IsActive)
                {
                    try
                    {
                        await transaction.RollbackAsync(context.CancellationToken).ConfigureAwait(false);
                    }
                    catch (Exception rollbackException)
                    {
                        LogContext.Warning?.Log(rollbackException, "Failed to rollback transaction");
                    }
                }

                throw;
            }
            finally
            {
                transaction?.Dispose();

                session.Dispose();
            }
        }
Example #26
0
        async Task IFilter <ConsumeContext <TMessage> > .Send(ConsumeContext <TMessage> context, IPipe <ConsumeContext <TMessage> > next)
        {
            Stopwatch timer = Stopwatch.StartNew();

            var activity = LogContext.IfEnabled(OperationName.Saga.SendQuery)?.StartActivity(new
            {
                SagaType    = TypeMetadataCache <TSaga> .ShortName,
                MessageType = TypeMetadataCache <TMessage> .ShortName
            });

            try
            {
                ISagaQuery <TSaga> query = _queryFactory.CreateQuery(context);

                SagaQueryConsumeContext <TSaga, TMessage> queryContext = new SagaQueryConsumeContextScope <TSaga, TMessage>(context, query);

                await Task.Yield();

                await _sagaRepository.SendQuery(queryContext, _policy, _messagePipe).ConfigureAwait(false);

                await next.Send(context).ConfigureAwait(false);

                await context.NotifyConsumed(timer.Elapsed, TypeMetadataCache <TSaga> .ShortName).ConfigureAwait(false);
            }
            catch (OperationCanceledException exception)
            {
                await context.NotifyFaulted(timer.Elapsed, TypeMetadataCache <TSaga> .ShortName, exception).ConfigureAwait(false);

                if (exception.CancellationToken == context.CancellationToken)
                {
                    throw;
                }

                throw new ConsumerCanceledException($"The operation was cancelled by the consumer: {TypeMetadataCache<TSaga>.ShortName}");
            }
            catch (Exception ex)
            {
                await context.NotifyFaulted(timer.Elapsed, TypeMetadataCache <TSaga> .ShortName, ex).ConfigureAwait(false);

                throw;
            }
            finally
            {
                activity?.Stop();
            }
        }
Example #27
0
        public async Task <SagaRepositoryQueryContext <TSaga> > Query(ISagaQuery <TSaga> query, CancellationToken cancellationToken = default)
        {
            QueryRequestOptions queryOptions = null;

            if (_context.QueryRequestOptions != null)
            {
                queryOptions = new QueryRequestOptions();
                _context.QueryRequestOptions?.Invoke(queryOptions);
            }

            // This will not work for Document Db because the .Where needs to look for [JsonProperty("id")],
            // and if you pass in CorrelationId property, the ISaga doesn't have that property. Can we .Select() it out?
            IEnumerable <TSaga> instances = await _context.Container.GetItemLinqQueryable <TSaga>(requestOptions : queryOptions)
                                            .Where(query.FilterExpression)
                                            .QueryAsync(cancellationToken)
                                            .ConfigureAwait(false);

            return(new LoadedSagaRepositoryQueryContext <TSaga>(this, instances));
        }
Example #28
0
        public async Task SendQuery <T>(ConsumeContext <T> context, ISagaQuery <TSaga> query, IPipe <SagaRepositoryQueryContext <TSaga, T> > next)
            where T : class
        {
            var dbContext = _dbContextFactory.CreateScoped(context);

            try
            {
                async Task Send()
                {
                    SagaLockContext <TSaga> lockContext = await _lockStrategy.CreateLockContext(dbContext, query, context.CancellationToken).ConfigureAwait(false);

                    var repositoryContext = new DbContextSagaRepositoryContext <TSaga, T>(dbContext, context, _consumeContextFactory, _lockStrategy);

                    await WithinTransaction(dbContext, context.CancellationToken, async() =>
                    {
                        IList <TSaga> instances = await lockContext.Load().ConfigureAwait(false);

                        var queryContext = new LoadedSagaRepositoryQueryContext <TSaga, T>(repositoryContext, instances);

                        await next.Send(queryContext).ConfigureAwait(false);
                    }).ConfigureAwait(false);
                }

                var executionStrategy = dbContext.Database.CreateExecutionStrategy();
                if (executionStrategy is ExecutionStrategy)
                {
                    await executionStrategy.ExecuteAsync(Send).ConfigureAwait(false);
                }
                else
                {
                    await Send().ConfigureAwait(false);
                }
            }
            finally
            {
                await _dbContextFactory.ReleaseAsync(dbContext).ConfigureAwait(false);
            }
        }
        /// <summary>
        /// Waits until a saga exists with the specified correlationId in the specified state
        /// </summary>
        /// <param name="correlationId"></param>
        /// <param name="state">The expected state</param>
        /// <param name="timeout"></param>
        /// <returns></returns>
        public async Task <Guid?> Exists(Guid correlationId, State state, TimeSpan?timeout = default)
        {
            if (QuerySagaRepository == null)
            {
                throw new InvalidOperationException("The repository does not support Query operations");
            }

            var giveUpAt = DateTime.Now + (timeout ?? TestTimeout);

            ISagaQuery <TInstance> query = _stateMachine.CreateSagaQuery(x => x.CorrelationId == correlationId, state);

            while (DateTime.Now < giveUpAt)
            {
                var saga = (await QuerySagaRepository.Find(query).ConfigureAwait(false)).FirstOrDefault();
                if (saga != Guid.Empty)
                {
                    return(saga);
                }

                await Task.Delay(10).ConfigureAwait(false);
            }

            return(default);
Example #30
0
 public Task <IEnumerable <Guid> > Find(ISagaQuery <TSaga> query)
 {
     return(Task.FromResult(_sagas.Where(query).Select(x => x.Instance.CorrelationId)));
 }