public async Task FactsAdded(ImmutableList <Fact> added, FactGraph graph, CancellationToken cancellationToken) { if (initialize == null) { return; } await initialize; var productsAdded = ImmutableList <(Product product, Inverse inverse)> .Empty; var productsRemoved = ImmutableList <Product> .Empty; var startReferences = added.Select(a => a.Reference).ToImmutableList(); foreach (var inverse in inverses) { var inversePipeline = inverse.InversePipeline; var matchingReferences = startReferences .Where(r => inversePipeline.Starts.Any(start => r.Type == start.Type)) .ToImmutableList(); if (matchingReferences.Any()) { var products = inversePipeline.CanRunOnGraph ? inversePipeline.Execute(startReferences, graph) : await factManager.Query( startReferences, new Specification(inversePipeline, specification.Projection), cancellationToken); foreach (var product in products) { var initialProduct = inverse.InitialSubset.Of(product); var identifyingProduct = inverse.FinalSubset.Of(product); if (initialProduct.Equals(this.initialAnchor)) { if (inverse.Operation == Operation.Add) { productsAdded = productsAdded.Add((identifyingProduct, inverse)); } else if (inverse.Operation == Operation.Remove) { productsRemoved = productsRemoved.Add(identifyingProduct); } } } } } if (productsAdded.Any()) { var products = productsAdded.Select(p => p.product).ToImmutableList(); var addedGraph = await factManager.LoadProducts(products, cancellationToken); var productAnchorProjections = DeserializeAllProducts(graph, productsAdded); var removals = await observation.NotifyAdded(productAnchorProjections); lock (this) { removalsByProduct = removalsByProduct.AddRange(removals); } } if (productsRemoved.Any()) { var removals = productsRemoved .Select(product => removalsByProduct.GetValueOrDefault(product) !) .Where(identity => identity != null) .ToImmutableList(); foreach (var removal in removals) { await removal(); } lock (this) { removalsByProduct = removalsByProduct.RemoveRange(productsRemoved); } } }