Beispiel #1
0
        private static RuntimePart CreateRuntimePart(ComposedPart part, CompositionConfiguration configuration)
        {
            Requires.NotNull(part, nameof(part));

            var runtimePart = new RuntimePart(
                part.Definition.TypeRef,
                part.Definition.ImportingConstructorOrFactoryRef,
                part.GetImportingConstructorImports().Select(kvp => CreateRuntimeImport(kvp.Key, kvp.Value, part.Resolver)).ToImmutableArray(),
                part.Definition.ImportingMembers.Select(idb => CreateRuntimeImport(idb, part.SatisfyingExports[idb], part.Resolver)).ToImmutableArray(),
                part.Definition.ExportDefinitions.Select(ed => CreateRuntimeExport(ed.Value, part.Definition.Type, ed.Key, part.Resolver)).ToImmutableArray(),
                part.Definition.OnImportsSatisfiedRef,
                part.Definition.IsShared ? configuration.GetEffectiveSharingBoundary(part.Definition) : null);

            return(runtimePart);
        }
        private static RuntimePart CreateRuntimePart(ComposedPart part, CompositionConfiguration configuration)
        {
            Requires.NotNull(part, nameof(part));

            var partDefinitionType   = part.Definition.Type;
            var importingConstructor = part.Definition.ImportingConstructorInfo;
            var onImportsSatisfied   = part.Definition.OnImportsSatisfied;
            var runtimePart          = new RuntimePart(
                TypeRef.Get(partDefinitionType, part.Resolver),
                importingConstructor != null ? new ConstructorRef(importingConstructor, part.Resolver) : default(ConstructorRef),
                part.GetImportingConstructorImports().Select(kvp => CreateRuntimeImport(kvp.Key, kvp.Value, part.Resolver)).ToImmutableArray(),
                part.Definition.ImportingMembers.Select(idb => CreateRuntimeImport(idb, part.SatisfyingExports[idb], part.Resolver)).ToImmutableArray(),
                part.Definition.ExportDefinitions.Select(ed => CreateRuntimeExport(ed.Value, partDefinitionType, ed.Key, part.Resolver)).ToImmutableArray(),
                onImportsSatisfied != null ? new MethodRef(onImportsSatisfied, part.Resolver) : default(MethodRef),
                part.Definition.IsShared ? configuration.GetEffectiveSharingBoundary(part.Definition) : null);

            return(runtimePart);
        }
        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));
        }
Beispiel #4
0
 public ComposedPartDiagnostic(ComposedPart part, string unformattedMessage, params object?[] args)
     : this(part, string.Format(CultureInfo.CurrentCulture, unformattedMessage, args))
 {
 }
Beispiel #5
0
 public ComposedPartDiagnostic(ComposedPart part, string formattedMessage)
     : this(ImmutableHashSet.Create(part), formattedMessage)
 {
 }