in src/Microsoft.VisualStudio.Composition/CompositionConfiguration.cs [75:203]
public static CompositionConfiguration Create(ComposableCatalog catalog)
{
Requires.NotNull(catalog, nameof(catalog));
// We consider all the parts in the catalog, plus the specially synthesized ones
// that should always be applied.
var customizedCatalog = catalog.AddParts(AlwaysBundledParts);
// Construct our part builders, initialized with all their imports satisfied.
// We explicitly use reference equality because ComposablePartDefinition.Equals is too slow, and unnecessary for this.
var partBuilders = new Dictionary<ComposablePartDefinition, PartBuilder>(ReferenceEquality<ComposablePartDefinition>.Default);
foreach (ComposablePartDefinition partDefinition in customizedCatalog.Parts)
{
var satisfyingImports = partDefinition.Imports.ToImmutableDictionary(i => i, i => customizedCatalog.GetExports(i.ImportDefinition));
partBuilders.Add(partDefinition, new PartBuilder(partDefinition, satisfyingImports));
}
// Create a lookup table that gets all immediate importers for each part.
foreach (PartBuilder partBuilder in partBuilders.Values)
{
// We want to understand who imports each part so we can properly propagate sharing boundaries
// for MEFv1 attributed parts. ExportFactory's that create sharing boundaries are an exception
// because if a part has a factory that creates new sharing boundaries, the requirement for
// that sharing boundary of the child scope shouldn't be interpreted as a requirement for that
// same boundary by the parent part.
// However, if the ExportFactory does not create sharing boundaries, it does in fact need all
// the same sharing boundaries as the parts it constructs.
var importedPartsExcludingFactoriesWithSharingBoundaries =
(from entry in partBuilder.SatisfyingExports
where !entry.Key.IsExportFactory || entry.Key.ImportDefinition.ExportFactorySharingBoundaries.Count == 0
from export in entry.Value
select export.PartDefinition).Distinct(ReferenceEquality<ComposablePartDefinition>.Default);
foreach (var importedPartDefinition in importedPartsExcludingFactoriesWithSharingBoundaries)
{
var importedPartBuilder = partBuilders[importedPartDefinition];
importedPartBuilder.ReportImportingPart(partBuilder);
}
}
// Propagate sharing boundaries defined on parts to all importers (transitive closure).
foreach (PartBuilder partBuilder in partBuilders.Values)
{
partBuilder.ApplySharingBoundary();
}
var sharingBoundaryOverrides = ComputeInferredSharingBoundaries(partBuilders.Values);
// Build up our set of composed parts.
var partsBuilder = ImmutableHashSet.CreateBuilder<ComposedPart>();
foreach (var partBuilder in partBuilders.Values)
{
var composedPart = new ComposedPart(partBuilder.PartDefinition, partBuilder.SatisfyingExports, partBuilder.RequiredSharingBoundaries.ToImmutableHashSet());
partsBuilder.Add(composedPart);
}
var parts = partsBuilder.ToImmutable();
// Determine which metadata views to use for each applicable import.
var metadataViewsAndProviders = GetMetadataViewProvidersMap(customizedCatalog);
// Validate configuration.
var errors = new List<ComposedPartDiagnostic>();
foreach (var part in parts)
{
errors.AddRange(part.Validate(metadataViewsAndProviders));
}
// Detect loops of all non-shared parts.
errors.AddRange(FindLoops(parts));
// If errors are found, re-validate the salvaged parts in case there are parts whose dependencies are affected by the initial errors
if (errors.Count > 0)
{
// Set salvaged parts to current catalog in case there are no errors
var salvagedParts = parts;
var salvagedPartDefinitions = catalog.Parts;
List<ComposedPartDiagnostic> previousErrors = errors;
Stack<IReadOnlyCollection<ComposedPartDiagnostic>> stackedErrors = new Stack<IReadOnlyCollection<ComposedPartDiagnostic>>();
// While we still find errors we validate the exports so that we remove all dependency failures
while (previousErrors.Count > 0)
{
stackedErrors.Push(previousErrors);
// Get the salvaged parts
var invalidParts = previousErrors.SelectMany(error => error.Parts).ToList();
if (invalidParts.Count == 0)
{
// If we can't identify the faulty parts but we still have errors, we have to just throw.
throw new CompositionFailedException(Strings.FailStableComposition, ImmutableStack.Create<IReadOnlyCollection<ComposedPartDiagnostic>>(errors));
}
salvagedParts = salvagedParts.Except(invalidParts);
var invalidPartDefinitionsSet = new HashSet<ComposablePartDefinition>(invalidParts.Select(p => p.Definition));
salvagedPartDefinitions = salvagedPartDefinitions.Except(invalidPartDefinitionsSet);
// Empty the list so that we create a new one only with the new set of errors
previousErrors = new List<ComposedPartDiagnostic>();
foreach (var part in salvagedParts)
{
previousErrors.AddRange(part.RemoveSatisfyingExports(invalidPartDefinitionsSet));
}
}
var finalCatalog = ComposableCatalog.Create(catalog.Resolver).AddParts(salvagedPartDefinitions);
// We want the first found errors to come out of the stack first, so we need to invert the current stack.
var compositionErrors = ImmutableStack.CreateRange(stackedErrors);
var configuration = new CompositionConfiguration(
finalCatalog,
salvagedParts,
metadataViewsAndProviders,
compositionErrors,
sharingBoundaryOverrides);
return configuration;
}
return new CompositionConfiguration(
catalog,
parts,
metadataViewsAndProviders,
ImmutableStack<IReadOnlyCollection<ComposedPartDiagnostic>>.Empty,
sharingBoundaryOverrides);
}