private void DoProcessQueue()
        {
            // begin read model transaction scope
            // get events for workitem.AggregateId
            // pass events to read model
            // commit

            // we might see a work item for an aggregate twice in a row
            // especially if we poke the queue when we receive events on
            // the event bus
            // in fact we can subscibe to events on the event bus and do our own poking

            var doMore = true;

            while (doMore)
            {
                var scope = this.scopeFactory();
                using (scope)
                {
                    var queue     = this.queueFactory(scope);
                    var workItems = queue.Peek(this.maxItems);
                    var bus       = new UnitOfWorkEventBus(this.context.EventBus);
                    scope.Add(bus);

                    foreach (var workItem in workItems)
                    {
                        if (this.cancel.IsCancellationRequested)
                        {
                            return;
                        }

                        var registration = this.registrations.FirstOrDefault(x => AggregateRootBase.GetAggregateTypeDescriptor(x.AggregateType) == workItem.AggregateType);
                        if (registration != null)
                        {
                            try
                            {
                                this.ProcessWorkItem(scope, registration, bus, workItem);
                                queue.Dequeue(workItem);
                            }
                            catch (Exception ex)
                            {
                                // TODO: handle sqlite busy exceptions
                                var topic = new DomainTopic(registration.AggregateType, workItem.Identity);
                                this.context.EventBus.Publish(new DomainNotification(topic, new ReadModelBuilderFaultedEvent(workItem.Identity, ex)));
                                throw;
                            }
                        }
                    }

                    scope.Commit();
                    doMore = workItems.Count == this.maxItems;
                }
            }
        }
        private void EnqueueReadModels(IReadModelQueueProducer queue, string aggregateType, IList <IAggregateEvent> events)
        {
            if (queue != null)
            {
                var groupedEvents = from evt in events
                                    group evt by evt.AggregateId into g
                                    select new { RootId = g.Key, Version = g.Min(x => x.Version) };

                foreach (var g in groupedEvents)
                {
                    var workItem = queue.Enqueue(g.RootId, aggregateType, g.Version);
                    if (workItem != null)
                    {
                        var topic = new DomainTopic(this.registration.AggregateType, g.RootId);
                        this.eventBus.Publish(new DomainNotification(topic, workItem));
                    }
                }
            }
        }
        private void DoProcessQueue()
        {
            // begin read model transaction scope
            // get events for workitem.AggregateId
            // pass events to read model
            // commit

            // we might see a work item for an aggregate twice in a row
            // especially if we poke the queue when we receive events on 
            // the event bus
            // in fact we can subscibe to events on the event bus and do our own poking

            var doMore = true;
            while (doMore)
            {
                var scope = this.scopeFactory();
                using (scope)
                {
                    var queue = this.queueFactory(scope);
                    var workItems = queue.Peek(this.maxItems);
                    var bus = new UnitOfWorkEventBus(this.context.EventBus);
                    scope.Add(bus);

                    foreach (var workItem in workItems)
                    {
                        if (this.cancel.IsCancellationRequested)
                        {
                            return;
                        }

                        var registration = this.registrations.FirstOrDefault(x => AggregateRootBase.GetAggregateTypeDescriptor(x.AggregateType) == workItem.AggregateType);
                        if (registration != null)
                        {
                            try
                            {
                                this.ProcessWorkItem(scope, registration, bus, workItem);
                                queue.Dequeue(workItem);
                            }
                            catch (Exception ex)
                            {
                                // TODO: handle sqlite busy exceptions
                                var topic = new DomainTopic(registration.AggregateType, workItem.Identity);
                                this.context.EventBus.Publish(new DomainNotification(topic, new ReadModelBuilderFaultedEvent(workItem.Identity, ex)));
                                throw;
                            }
                        }
                    }

                    scope.Commit();
                    doMore = workItems.Count == this.maxItems;
                }
            }
        }
        private void EnqueueReadModels(IReadModelQueueProducer queue, string aggregateType, IList<IAggregateEvent> events)
        {
            if (queue != null)
            {
                var groupedEvents = from evt in events
                    group evt by evt.AggregateId into g
                select new { RootId = g.Key, Version = g.Min(x => x.Version)};

                foreach (var g in groupedEvents)
                {
                    var workItem = queue.Enqueue(g.RootId, aggregateType, g.Version);
                    if (workItem != null)
                    {
                        var topic = new DomainTopic(this.registration.AggregateType, g.RootId);
                        this.eventBus.Publish(new DomainNotification(topic, workItem));
                    }
                }
            }
        }