public void BuildIndexFromGraphs(CloudTableClient cloudTableClient) { //Let the background thread claim the wait handle. ThreadPool.QueueUserWorkItem( state => { var registeredGraphs = registeredIndexer.GraphsIndexed; var session = new InternalSession(registeredIndexer.Registry, backingStore); //We use the session to manage to loading and deserialisation of graphs that contribute to the index... session .GetEntireStash() .Matching( _ => _.Where<StashTypeHierarchy>().AnyOf(registeredGraphs.Select(rg => StashTypeHierarchy.GetConcreteTypeValue(rg.GraphType)))) .Materialize(); //...but we then use the enrolled persistence events directly to yield the keys for this new index. var graphProjections = session .EnrolledPersistenceEvents .SelectMany( trackedGraph => registeredIndexer.GetUntypedProjections(trackedGraph.UntypedGraph), (graph, projection) => new {Projection = (ProjectedIndex)projection, graph.InternalId}); backingStore.InTransactionDo( work => { foreach(var projection in graphProjections) { try { Insert(projection.Projection.UntypedKey, projection.InternalId, ((AzureStorageWork)work).ServiceContext); } catch(InvalidOperationException ioEx) { var storageEx = ioEx.TranslateDataServiceClientException(); if (storageEx.ExtendedErrorInformation.ErrorCode != TableErrorCodeStrings.EntityAlreadyExists) throw storageEx; //This is acceptable, as it is possible for another session to insert the key //before we match it. We carry on. } } }); session.Abandon(); indexCompiled.Set(); }); }