예제 #1
0
        internal static IQueryable <StorableEvent> Where(
            this IQueryable <StorableEvent> eventQuery,
            MatchEvent[] matchEvents,
            Expression <Func <StorableEvent, bool> > filter)
        {
            matchEvents = matchEvents ?? new[] { new MatchEvent() };

            // if specific event types are requested, we can optimize the event store query
            // if Event or IEvent are requested, we don't filter -- this requires reading every event
            if (matchEvents.Any())
            {
                var eventTypes = matchEvents.Select(m => m.Type).Distinct().ToArray();
                var aggregates = matchEvents.Select(m => m.StreamName).Distinct().ToArray();

                if (!aggregates.Any(streamName => string.IsNullOrWhiteSpace(streamName) || streamName == MatchEvent.Wildcard))
                {
                    if (!eventTypes.Any(type => string.IsNullOrWhiteSpace(type) || type == MatchEvent.Wildcard))
                    {
                        // Filter on StreamName and Type
                        var projectionEventFilter = new CatchupEventFilter(matchEvents);
                        eventQuery = eventQuery.Where(projectionEventFilter.Filter);
                    }
                    else
                    {
                        // Filter on StreamName
                        eventQuery = eventQuery.Where(e => aggregates.Contains(e.StreamName));
                    }
                }
            }

            if (filter != null)
            {
                eventQuery = eventQuery.Where(filter);
            }

            return(eventQuery);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ExclusiveEventStoreCatchupQuery" /> class.
        /// </summary>
        /// <param name="dbContext">The event store database context to execute the query against.</param>
        /// <param name="lockResourceName">Name of the lock. Multiple instances compete with other instances having the same <paramref name="lockResourceName" />.</param>
        /// <param name="getStartAtId">The id of the first event to query.</param>
        /// <param name="matchEvents">Specifies the event types to include the query. If none are specified, all events are queried.</param>
        /// <param name="batchSize">The number of events queried from the event store at each iteration.</param>
        /// <param name="filter">An optional filter expression to constrain the query that the catchup uses over the event store.</param>
        public ExclusiveEventStoreCatchupQuery(
            EventStoreDbContext dbContext,
            string lockResourceName,
            Func <long> getStartAtId,
            MatchEvent[] matchEvents,
            int batchSize = 10000,
            Expression <Func <StorableEvent, bool> > filter = null)
        {
            if (batchSize < 1)
            {
                throw new ArgumentException($"{nameof(batchSize)} must be greater than zero.");
            }

            this.dbContext        = dbContext;
            this.lockResourceName = lockResourceName;

            if (TryGetAppLock())
            {
                startAtId = getStartAtId();
                IQueryable <StorableEvent> eventQuery = dbContext.Events.AsNoTracking();

                matchEvents = matchEvents ?? new[] { new MatchEvent() };

                // if specific event types are requested, we can optimize the event store query
                // if Event or IEvent are requested, we don't filter -- this requires reading every event
                if (matchEvents.Any())
                {
                    var eventTypes = matchEvents.Select(m => m.Type).Distinct().ToArray();
                    var aggregates = matchEvents.Select(m => m.StreamName).Distinct().ToArray();

                    if (!aggregates.Any(streamName => string.IsNullOrWhiteSpace(streamName) || streamName == MatchEvent.Wildcard))
                    {
                        if (!eventTypes.Any(type => string.IsNullOrWhiteSpace(type) || type == MatchEvent.Wildcard))
                        {
                            // Filter on StreamName and Type
                            var projectionEventFilter = new CatchupEventFilter(matchEvents);
                            eventQuery = eventQuery.Where(projectionEventFilter.Filter);
                        }
                        else
                        {
                            // Filter on StreamName
                            eventQuery = eventQuery.Where(e => aggregates.Contains(e.StreamName));
                        }
                    }
                }

                if (filter != null)
                {
                    eventQuery = eventQuery.Where(filter);
                }

                eventQuery = eventQuery
                             .Where(e => e.Id >= startAtId)
                             .Take(batchSize);

                expectedNumberOfEvents = eventQuery.Count();

                events = DurableStreamFrom(eventQuery, startAtId);
            }
            else
            {
                events = Enumerable.Empty <StorableEvent>();
            }
        }