static void ObserveWhileCan(IEnumerable <StoreRecord> records, RedirectToDynamicEvent wire, CancellationToken token) { var watch = Stopwatch.StartNew(); int count = 0; foreach (var record in records) { count += 1; if (token.IsCancellationRequested) { return; } if (count % 50000 == 0) { SystemObserver.Notify("Observing {0} {1}", count, Math.Round(watch.Elapsed.TotalSeconds, 2)); watch.Restart(); } foreach (var message in record.Items) { if (message is IEvent) { wire.InvokeEvent(message); } } } }
public void when_wire_to_object_where_argument_is_interface() { var dynamicEvent = new RedirectToDynamicEvent(); var testClass = new TestClassWithInterfaceMethod(); dynamicEvent.WireToWhen(testClass); }
private long ObserveWhileCan(IEnumerable <StoreRecord> records, RedirectToDynamicEvent wire, CancellationToken token) { var watch = Stopwatch.StartNew(); var count = 0; long eventStoreVersion = 0; foreach (var record in records) { count += 1; if (token.IsCancellationRequested) { return(eventStoreVersion); } if (count % 50000 == 0) { SystemObserver.Notify("[observing]\t{0}\t{1} records in {2} seconds", this._name, count, Math.Round(watch.Elapsed.TotalSeconds, 2)); watch.Restart(); } foreach (var message in record.Items.OfType <TEvent>()) { wire.InvokeEvent(message); } eventStoreVersion = record.StoreVersion; } return(eventStoreVersion); }
static void CallHandlers(RedirectToDynamicEvent functions, ImmutableEnvelope aem) { var e = aem.Message as IEvent; if (e != null) { functions.InvokeEvent(e); } }
public void when_wire_to_action() { var dynamicEvent = new RedirectToDynamicEvent(); var testClass = new TestClassWithMethod(); dynamicEvent.WireTo<int>(i => { testClass.Summa += i; }); dynamicEvent.InvokeEvent(4); dynamicEvent.InvokeEvent(4); Assert.AreEqual(8, testClass.Summa); }
public void when_wire_to_object() { var dynamicEvent = new RedirectToDynamicEvent(); var testClass = new TestClassWithMethod(); dynamicEvent.WireToWhen(testClass); var t = new TestRedirectMethod { Id = 333, Name = "Name" }; dynamicEvent.InvokeEvent(t); Assert.AreEqual(t, testClass.RedirectMethod); }
public void when_wire_to_action() { var dynamicEvent = new RedirectToDynamicEvent(); var testClass = new TestClassWithMethod(); dynamicEvent.WireTo <int>(i => { testClass.Summa += i; }); dynamicEvent.InvokeEvent(4); dynamicEvent.InvokeEvent(4); Assert.AreEqual(8, testClass.Summa); }
static void CallHandlers(RedirectToDynamicEvent functions, ImmutableEnvelope aem) { if (aem.Items.Length != 1) throw new InvalidOperationException( "Unexpected number of items in envelope that arrived to projections: " + aem.Items.Length); // we wire envelope contents to both direct message call and sourced call (with date wrapper) var content = aem.Items[0].Content; functions.InvokeEvent(content); functions.InvokeEvent(Source.For(aem.EnvelopeId, aem.CreatedOnUtc, (IRecipeEvent) content)); }
public AssembledComponents AssembleComponents() { var documents = CreateNuclear(new DocumentStrategy()); var streamer = Streamer; var tapes = Tapes; var streaming = Streaming; var routerQueue = CreateQueueWriter(Topology.RouterQueue); var aggregates = new AggregateFactory(tapes, streamer, routerQueue); var sender = new SimpleMessageSender(streamer, routerQueue); var flow = new MessageSender(sender); var builder = new CqrsEngineBuilder(streamer); var log = tapes.GetOrCreateStream(Topology.DomainLogName); var dispatcher = new SimpleDispatcher(aggregates, streamer, log); builder.Handle(CreateInbox(Topology.RouterQueue), Topology.Route(CreateQueueWriter, streamer, tapes), "router"); builder.Handle(CreateInbox(Topology.EntityQueue), aggregates.Dispatch); var functions = new RedirectToDynamicEvent(); // documents //functions.WireToWhen(new RegistrationUniquenessProjection(atomic.Factory.GetEntityWriter<unit, RegistrationUniquenessDocument>())); // UI projections var projectionStore = CreateNuclear(new ProjectionStrategy()); foreach (var projection in BootstrapProjections.BuildProjectionsWithWhenConvention(projectionStore.Factory)) { functions.WireToWhen(projection); } // processes //functions.WireToWhen(new Domain.RecipeProcess(flow)); builder.Handle(CreateInbox(Topology.EventsQueue), aem => CallHandlers(functions, aem)); var timer = new StreamingTimerService(CreateQueueWriter(Topology.RouterQueue), streaming.GetContainer(Topology.FutureMessagesContainer), streamer); builder.Handle(CreateInbox(Topology.TimerQueue), timer.PutMessage); builder.AddProcess(timer); // GNS: just for this simple sample. In reality one would have some command handlers involved Bus.SetBus(new SimpleBus(sender, dispatcher)); return new AssembledComponents { Builder = builder, Sender = sender, Dispatcher = dispatcher, ProjectionFactory = projectionStore.Factory, Setup = this }; }
static void CallHandlers(RedirectToDynamicEvent functions, ImmutableEnvelope aem) { if (aem.Items.Length != 1) { throw new InvalidOperationException( "Unexpected number of items in envelope that arrived to projections: " + aem.Items.Length); } // we wire envelope contents to both direct message call and sourced call (with date wrapper) var content = aem.Items[0].Content; functions.InvokeEvent(content); }
DirectoryInfo[] ObserveViews(ViewMapInfo[] handlers, DomainQueryManager queryManager) { WriteLine("Configuring {0} view handlers", handlers.Length); var name = string.Format("view-{0:yyyy-MM-dd-HH-mm-ss}", DateTime.Now); var combined = Path.Combine(Path.GetTempPath(), name); WriteLine("Redirecting atomic views to {0}", combined); var directoryInfo = new DirectoryInfo(combined); var file = FileStorage.CreateConfig(combined, reset: true); var nuclear = file.CreateNuclear(new ProjectionStrategy()); var watch = Stopwatch.StartNew(); var projectionTypes = new HashSet <Type>(handlers.Select(v => v.Projection)); WriteLine("Building consumers..."); var projections = BootstrapProjections.BuildProjectionsWithWhenConvention(nuclear.Factory).Where( x => projectionTypes.Contains(x.GetType())); var wire = new RedirectToDynamicEvent(); foreach (var projection in projections) { wire.WireToWhen(projection); } var handlersWatch = Stopwatch.StartNew(); queryManager.QueryConsumer(envelope => CallHandlers(wire, envelope)); WriteLine("Saved to {0}.", directoryInfo.FullName); var timeTotal = watch.Elapsed.TotalSeconds; var handlerTicks = handlersWatch.ElapsedTicks; var timeInHandlers = Math.Round(TimeSpan.FromTicks(handlerTicks).TotalSeconds, 1); WriteLine("Total Elapsed: {0}sec ({1}sec in handlers)", Math.Round(timeTotal, 0), timeInHandlers); WriteLine("Saved to {0}.", directoryInfo.FullName); var directoryInfos = handlers .Select(h => _strategy.GetFolderForEntity(h.ViewType, h.KeyType)) .Distinct() .Select(f => new DirectoryInfo(Path.Combine(directoryInfo.FullName, f.ToString()))) .ToArray(); return(directoryInfos); }
static void CallHandlers(RedirectToDynamicEvent functions, ImmutableEnvelope aem) { foreach (var item in aem.Items) { var e = item.Content as ISampleEvent; if (e != null) { // we wire envelope contents to both direct message call and sourced call (with date wrapper) functions.InvokeEvent(e); functions.InvokeEvent(Source.For(aem.EnvelopeId, aem.CreatedOnUtc, e)); } } }
public EventsChasingService(string name, IEventStoreCheckpoint storeChekpoint, object projection, IBufferedDocumentWriter writer, IDocumentStore viewsStorage, MessageStore eventsStore) { Condition.Requires(name, "name").IsNotNull(); Condition.Requires(eventsStore, "eventsStore").IsNotNull(); Condition.Requires(projection, "projection").IsNotNull(); Condition.Requires(writer, "writer").IsNotNull(); Condition.Requires(viewsStorage, "viewsStorage").IsNotNull(); Condition.Requires(eventsStore, "eventsStore").IsNotNull(); this._name = name; this._storeChekpoint = storeChekpoint; this._writer = writer; this._eventsStore = eventsStore; this._projectionName = projection.GetType().Name; this._eventHandlers = new RedirectToDynamicEvent(); this._eventHandlers.WireToWhen(projection); }
public static IEnumerable <ViewMapInfo> GetActiveViewMaps() { var marker = new AtomicDetector(); var projections = BootstrapProjections.BuildProjectionsWithWhenConvention(marker) .ToArray(); int idx = 0; foreach (var projection in projections) { var redirect = new RedirectToDynamicEvent(); redirect.WireToWhen(projection); var mark = marker.List[idx]; yield return(new ViewMapInfo(mark.Item2, mark.Item1, projection.GetType(), redirect.Dict.Keys.ToArray()) ); idx += 1; } }
private Task RebuildProjections(List <ProjectionInfo> needRebuild, IDocumentStore memoryContainer, CancellationToken token) { if (needRebuild.Count == 0) { return(Task.FromResult(true)); } var updateTimer = new Stopwatch(); updateTimer.Start(); // observe projections var watch = Stopwatch.StartNew(); var wire = new RedirectToDynamicEvent(); needRebuild.ForEach(x => wire.WireToWhen(x.GetTempProjection())); var handlersWatch = Stopwatch.StartNew(); var eventStoreVersion = this.ObserveWhileCan(this._eventsStore.EnumerateAllItems(0, int.MaxValue), wire, token); if (token.IsCancellationRequested) { SystemObserver.Notify("[warn]\t{0}\tShutdown. Aborting projections before anything was changed.", this._name); return(Task.FromResult(true)); } var timeTotal = watch.Elapsed.TotalSeconds; var handlerTicks = handlersWatch.ElapsedTicks; var timeInHandlers = Math.Round(TimeSpan.FromTicks(handlerTicks).TotalSeconds, 1); SystemObserver.Notify("[observe]\t{2}\t{0}sec ({1}sec in handlers) - Replayed events from", Math.Round(timeTotal, 0), timeInHandlers, this._name); var rebuildTasks = needRebuild.Select(projectionInfo => this.RebuildProjection(eventStoreVersion, projectionInfo, memoryContainer, token)); return(Task.WhenAll(rebuildTasks)); }
public AssembledComponents AssembleComponents() { // set up all the variables var routerQueue = CreateQueueWriter(Topology.RouterQueue); var commands = new RedirectToCommand(); var events = new RedirectToDynamicEvent(); IEventStore eventStore = null;// new LegacyTapeStreamEventStore(Tapes(Topology.TapesContainer), Streamer, routerQueue); var simple = new SimpleMessageSender(Streamer, routerQueue); var flow = new CommandSender(simple); var builder = new CqrsEngineBuilder(Streamer); // route queue infrastructure together builder.Handle(CreateInbox(Topology.RouterQueue), Topology.Route(CreateQueueWriter, Streamer, Tapes), "router"); builder.Handle(CreateInbox(Topology.EntityQueue), em => CallHandlers(commands, em)); builder.Handle(CreateInbox(Topology.EventsQueue), aem => CallHandlers(events, aem)); // message wiring magic DomainBoundedContext.ApplicationServices(Docs, eventStore).ForEach(commands.WireToWhen); DomainBoundedContext.Receptors(flow).ForEach(events.WireToWhen); DomainBoundedContext.Projections(Docs).ForEach(events.WireToWhen); DomainBoundedContext.Tasks(flow, Docs, false).ForEach(builder.AddTask); ClientBoundedContext.Projections(Docs).ForEach(events.WireToWhen); return(new AssembledComponents { Builder = builder, Sender = flow, Setup = this, Simple = simple }); }
static void Observe(ITapeStream tapes, RedirectToDynamicEvent wire) { var date = DateTime.MinValue; var watch = Stopwatch.StartNew(); foreach (var record in tapes.ReadRecords(0, int.MaxValue)) { var env = Streamer.ReadAsEnvelopeData(record.Data); if (date.Month != env.CreatedOnUtc.Month) { date = env.CreatedOnUtc; SystemObserver.Notify("Observing {0:yyyy-MM-dd} {1}", date, Math.Round(watch.Elapsed.TotalSeconds, 2)); watch.Restart(); } foreach (var item in env.Items) { var e = item.Content as IEvent; if (e != null) { wire.InvokeEvent(e); } } } }
public static void Rebuild(CancellationToken token, IDocumentStore targetContainer, MessageStore stream, Func <IDocumentStore, IEnumerable <object> > projectors) { var strategy = targetContainer.Strategy; var memory = new MemoryStorageConfig(); var memoryContainer = memory.CreateNuclear(strategy).Container; var tracked = new ProjectionInspectingStore(memoryContainer); var projections = new List <object>(); projections.AddRange(projectors(tracked)); if (tracked.Projections.Count != projections.Count()) { throw new InvalidOperationException("Count mismatch"); } tracked.ValidateSanity(); var storage = new NuclearStorage(targetContainer); var persistedHashes = new Dictionary <string, string>(); var name = "domain"; storage.GetEntity <ProjectionHash>(name).IfValue(v => persistedHashes = v.BucketHashes); var activeMemoryProjections = projections.Select((projection, i) => { var proj = tracked.Projections[i]; var bucketName = proj.StoreBucket; var viewType = proj.EntityType; var projectionHash = "Global change on 2012-08-24\r\n" + GetClassHash(projection.GetType()) + "\r\n " + GetClassHash(viewType) + "\r\n" + GetClassHash(strategy.GetType()); bool needsRebuild = !persistedHashes.ContainsKey(bucketName) || persistedHashes[bucketName] != projectionHash; return(new { bucketName, projection, hash = projectionHash, needsRebuild }); }).ToArray(); foreach (var memoryProjection in activeMemoryProjections) { if (memoryProjection.needsRebuild) { SystemObserver.Notify("[warn] {0} needs rebuild", memoryProjection.bucketName); } else { SystemObserver.Notify("[good] {0} is up-to-date", memoryProjection.bucketName); } } var needRebuild = activeMemoryProjections.Where(x => x.needsRebuild).ToArray(); if (needRebuild.Length == 0) { return; } var watch = Stopwatch.StartNew(); var wire = new RedirectToDynamicEvent(); needRebuild.ForEach(x => wire.WireToWhen(x.projection)); var handlersWatch = Stopwatch.StartNew(); ObserveWhileCan(stream.EnumerateAllItems(0, int.MaxValue), wire, token); if (token.IsCancellationRequested) { SystemObserver.Notify("[warn] Aborting projections before anything was changed"); return; } var timeTotal = watch.Elapsed.TotalSeconds; var handlerTicks = handlersWatch.ElapsedTicks; var timeInHandlers = Math.Round(TimeSpan.FromTicks(handlerTicks).TotalSeconds, 1); SystemObserver.Notify("Total Elapsed: {0}sec ({1}sec in handlers)", Math.Round(timeTotal, 0), timeInHandlers); // update projections that need rebuild foreach (var b in needRebuild) { // server might shut down the process soon anyway, but we'll be // in partially consistent mode (not all projections updated) // so at least we blow up between projection buckets token.ThrowIfCancellationRequested(); var bucketName = b.bucketName; var bucketHash = b.hash; // wipe contents targetContainer.Reset(bucketName); // write new versions var contents = memoryContainer.EnumerateContents(bucketName); targetContainer.WriteContents(bucketName, contents); // update hash storage.UpdateEntityEnforcingNew <ProjectionHash>(name, x => { x.BucketHashes[bucketName] = bucketHash; }); SystemObserver.Notify("[good] Updated View bucket {0}.{1}", name, bucketName); } // Clean up obsolete views var allBuckets = new HashSet <string>(activeMemoryProjections.Select(p => p.bucketName)); var obsoleteBuckets = persistedHashes.Where(s => !allBuckets.Contains(s.Key)).ToArray(); foreach (var hash in obsoleteBuckets) { // quit at this stage without any bad side effects if (token.IsCancellationRequested) { return; } var bucketName = hash.Key; SystemObserver.Notify("[warn] {0} is obsolete", bucketName); targetContainer.Reset(bucketName); storage.UpdateEntityEnforcingNew <ProjectionHash>(name, x => x.BucketHashes.Remove(bucketName)); SystemObserver.Notify("[good] Cleaned up obsolete view bucket {0}.{1}", name, bucketName); } }
public static void Rebuild(IDocumentStore targetContainer, ITapeStream stream) { var strategy = targetContainer.Strategy; var memory = new MemoryStorageConfig(); var memoryContainer = memory.CreateNuclear(strategy).Container; var tracked = new ProjectionInspectingContainer(memoryContainer); var projections = new List <object>(); projections.AddRange(DomainBoundedContext.Projections(tracked)); projections.AddRange(ClientBoundedContext.Projections(tracked)); //projections.AddRange(ApiOpsBoundedContext.Projections(tracked)); if (tracked.Buckets.Count != projections.Count()) { throw new InvalidOperationException("Count mismatch"); } var storage = new NuclearStorage(targetContainer); var hashes = storage.GetSingletonOrNew <ProjectionHash>().Entries; var memoryProjections = projections.Select((projection, i) => { var bucketName = tracked.Buckets[i]; var viewType = tracked.Views[i]; var projectionHash = GetClassHash(projection.GetType()) + "\r\n" + GetClassHash(viewType); bool needsRebuild = !hashes.ContainsKey(bucketName) || hashes[bucketName] != projectionHash; return(new { bucketName, projection, hash = projectionHash, needsRebuild }); }).ToArray(); foreach (var memoryProjection in memoryProjections) { if (memoryProjection.needsRebuild) { SystemObserver.Notify("[warn] {0} needs rebuild", memoryProjection.bucketName); } else { SystemObserver.Notify("[good] {0} is up-to-date", memoryProjection.bucketName); } } var needRebuild = memoryProjections.Where(x => x.needsRebuild).ToArray(); if (needRebuild.Length == 0) { return; } var watch = Stopwatch.StartNew(); var wire = new RedirectToDynamicEvent(); needRebuild.ForEach(x => wire.WireToWhen(x.projection)); var handlersWatch = Stopwatch.StartNew(); Observe(stream, wire); var timeTotal = watch.Elapsed.TotalSeconds; var handlerTicks = handlersWatch.ElapsedTicks; var timeInHandlers = Math.Round(TimeSpan.FromTicks(handlerTicks).TotalSeconds, 1); Console.WriteLine("Total Elapsed: {0}sec ({1}sec in handlers)", Math.Round(timeTotal, 0), timeInHandlers); // delete projections that were rebuilt var bucketNames = needRebuild.Select(x => x.bucketName).ToArray(); foreach (var name in bucketNames) { targetContainer.Reset(name); var contents = memoryContainer.EnumerateContents(name); targetContainer.WriteContents(name, contents); } var allBuckets = new HashSet <string>(memoryProjections.Select(p => p.bucketName)); var obsolete = hashes.Keys.Where(s => !allBuckets.Contains(s)).ToArray(); foreach (var name in obsolete) { SystemObserver.Notify("[warn] {0} is obsolete", name); targetContainer.Reset(name); } storage.UpdateSingletonEnforcingNew <ProjectionHash>(x => { x.Entries.Clear(); foreach (var prj in memoryProjections) { x.Entries[prj.bucketName] = prj.hash; } }); }
static void Main(string[] args) { const string integrationPath = @"temp"; ConfigureObserver(); var config = FileStorage.CreateConfig(integrationPath, "files"); var demoMessages = new List <ISampleMessage>(); var currentProcess = Process.GetCurrentProcess(); demoMessages.Add(new InstanceStarted("Inject git rev", currentProcess.ProcessName, currentProcess.Id.ToString())); { // wipe all folders config.Reset(); Console.WriteLine("Starting in funny mode"); var security = new SecurityId(0); demoMessages.Add(new CreateSecurityAggregate(security)); demoMessages.Add(new AddSecurityPassword(security, "Rinat Abdullin", "*****@*****.**", "password")); demoMessages.Add(new AddSecurityIdentity(security, "Rinat's Open ID", "http://abdullin.myopenid.org")); demoMessages.Add(new AddSecurityKey(security, "some key")); } var atomic = config.CreateNuclear(new DocumentStrategy()); var identity = new IdentityGenerator(atomic); var streamer = Contracts.CreateStreamer(); var tapes = config.CreateTape(Topology.TapesContainer); var streaming = config.CreateStreaming(); var routerQueue = config.CreateQueueWriter(Topology.RouterQueue); var aggregates = new AggregateFactory(tapes, streamer, routerQueue, atomic, identity); var sender = new SimpleMessageSender(streamer, routerQueue); var flow = new MessageSender(sender); var builder = new CqrsEngineBuilder(streamer); builder.Handle(config.CreateInbox(Topology.RouterQueue), Topology.Route(config.CreateQueueWriter, streamer, tapes), "router"); builder.Handle(config.CreateInbox(Topology.EntityQueue), aggregates.Dispatch); var functions = new RedirectToDynamicEvent(); // documents //functions.WireToWhen(new RegistrationUniquenessProjection(atomic.Factory.GetEntityWriter<unit, RegistrationUniquenessDocument>())); // UI projections var projectionStore = config.CreateNuclear(new ProjectionStrategy()); foreach (var projection in BootstrapProjections.BuildProjectionsWithWhenConvention(projectionStore.Factory)) { functions.WireToWhen(projection); } // processes //functions.WireToWhen(new BillingProcess(flow)); //functions.WireToWhen(new RegistrationProcess(flow)); functions.WireToWhen(new ReplicationProcess(flow)); builder.Handle(config.CreateInbox(Topology.EventsQueue), aem => CallHandlers(functions, aem)); var timer = new StreamingTimerService(config.CreateQueueWriter(Topology.RouterQueue), streaming.GetContainer(Topology.FutureMessagesContainer), streamer); builder.Handle(config.CreateInbox(Topology.TimerQueue), timer.PutMessage); builder.AddProcess(timer); using (var cts = new CancellationTokenSource()) using (var engine = builder.Build()) { var task = engine.Start(cts.Token); foreach (var sampleMessage in demoMessages) { sender.SendOne(sampleMessage); } Console.WriteLine(@"Press enter to stop"); Console.ReadLine(); cts.Cancel(); if (task.Wait(5000)) { Console.WriteLine(@"Terminating"); } } }
public Container Build() { var appendOnlyStore = CreateTapes(TapesContainer); var messageStore = new MessageStore(appendOnlyStore, Streamer.MessageSerializer); var toCommandRouter = new MessageSender(Streamer, CreateQueueWriter(RouterQueue)); var toFunctionalRecorder = new MessageSender(Streamer, CreateQueueWriter(FunctionalRecorderQueue)); var toEventHandlers = new MessageSender(Streamer, CreateQueueWriter(EventProcessingQueue)); var sender = new TypedMessageSender(toCommandRouter, toFunctionalRecorder); var store = new EventStore(messageStore); var quarantine = new EnvelopeQuarantine(Streamer, sender, Streaming.GetContainer(ErrorsContainer)); var builder = new CqrsEngineBuilder(Streamer, quarantine); var events = new RedirectToDynamicEvent(); var commands = new RedirectToCommand(); var funcs = new RedirectToCommand(); builder.Handle(CreateInbox(EventProcessingQueue), aem => CallHandlers(events, aem), "watch"); builder.Handle(CreateInbox(AggregateHandlerQueue), aem => CallHandlers(commands, aem)); builder.Handle(CreateInbox(RouterQueue), MakeRouter(messageStore), "watch"); // multiple service queues _serviceQueues.ForEach(s => builder.Handle(CreateInbox(s), aem => CallHandlers(funcs, aem))); builder.Handle(CreateInbox(FunctionalRecorderQueue), aem => RecordFunctionalEvent(aem, messageStore)); var viewDocs = CreateDocs(ViewStrategy); var stateDocs = new NuclearStorage(CreateDocs(DocStrategy)); var vector = new DomainIdentityGenerator(stateDocs); //var ops = new StreamOps(Streaming); var projections = new ProjectionsConsumingOneBoundedContext(); // Domain Bounded Context DomainBoundedContext.EntityApplicationServices(viewDocs, store, vector).ForEach(commands.WireToWhen); DomainBoundedContext.FuncApplicationServices().ForEach(funcs.WireToWhen); DomainBoundedContext.Ports(sender).ForEach(events.WireToWhen); DomainBoundedContext.Tasks(sender, viewDocs, true).ForEach(builder.AddTask); projections.RegisterFactory(DomainBoundedContext.Projections); // Client Bounded Context projections.RegisterFactory(ClientBoundedContext.Projections); // wire all projections projections.BuildFor(viewDocs).ForEach(events.WireToWhen); // wire in event store publisher var publisher = new MessageStorePublisher(messageStore, toEventHandlers, stateDocs, DoWePublishThisRecord); builder.AddTask(c => Task.Factory.StartNew(() => publisher.Run(c))); return(new Container { Builder = builder, Setup = this, SendToCommandRouter = toCommandRouter, MessageStore = messageStore, ProjectionFactories = projections, ViewDocs = viewDocs, Publisher = publisher, AppendOnlyStore = appendOnlyStore }); }