1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Collections.Generic; 6 using System.Composition.Convention; 7 using System.Composition.Hosting.Core; 8 using System.Composition.TypedParts.ActivationFeatures; 9 using System.Composition.TypedParts.Discovery; 10 using System.Linq; 11 using System.Reflection; 12 13 namespace System.Composition.TypedParts 14 { 15 internal class TypedPartExportDescriptorProvider : ExportDescriptorProvider 16 { 17 private readonly IDictionary<CompositionContract, ICollection<DiscoveredExport>> _discoveredParts = new Dictionary<CompositionContract, ICollection<DiscoveredExport>>(); 18 TypedPartExportDescriptorProvider(IEnumerable<Type> types, AttributedModelProvider attributeContext)19 public TypedPartExportDescriptorProvider(IEnumerable<Type> types, AttributedModelProvider attributeContext) 20 { 21 var activationFeatures = CreateActivationFeatures(attributeContext); 22 var typeInspector = new TypeInspector(attributeContext, activationFeatures); 23 24 foreach (var type in types) 25 { 26 DiscoveredPart part; 27 if (typeInspector.InspectTypeForPart(type.GetTypeInfo(), out part)) 28 { 29 AddDiscoveredPart(part); 30 } 31 } 32 } 33 AddDiscoveredPart(DiscoveredPart part)34 private void AddDiscoveredPart(DiscoveredPart part) 35 { 36 foreach (var export in part.DiscoveredExports) 37 { 38 AddDiscoveredExport(export); 39 } 40 } 41 AddDiscoveredExport(DiscoveredExport export, CompositionContract contract = null)42 private void AddDiscoveredExport(DiscoveredExport export, CompositionContract contract = null) 43 { 44 var actualContract = contract ?? export.Contract; 45 46 ICollection<DiscoveredExport> forKey; 47 if (!_discoveredParts.TryGetValue(actualContract, out forKey)) 48 { 49 forKey = new List<DiscoveredExport>(); 50 _discoveredParts.Add(actualContract, forKey); 51 } 52 53 forKey.Add(export); 54 } 55 GetExportDescriptors(CompositionContract contract, DependencyAccessor definitionAccessor)56 public override IEnumerable<ExportDescriptorPromise> GetExportDescriptors(CompositionContract contract, DependencyAccessor definitionAccessor) 57 { 58 DiscoverGenericParts(contract); 59 DiscoverConstrainedParts(contract); 60 61 ICollection<DiscoveredExport> forKey; 62 if (!_discoveredParts.TryGetValue(contract, out forKey)) 63 return NoExportDescriptors; 64 65 // Exports with metadata may be matched via metadata constraints. 66 // It should be possible to do this more aggressively by changing the way 67 // exports are stored. 68 if (!forKey.Any(x => x.Metadata.Any())) 69 { 70 // Allow some garbage to be collected 71 _discoveredParts.Remove(contract); 72 } 73 74 return forKey.Select(de => de.GetExportDescriptorPromise(contract, definitionAccessor)).ToArray(); 75 } 76 77 // If the contract has metadata constraints, look for exports with matching metadata. DiscoverConstrainedParts(CompositionContract contract)78 private void DiscoverConstrainedParts(CompositionContract contract) 79 { 80 if (contract.MetadataConstraints != null) 81 { 82 var unconstrained = new CompositionContract(contract.ContractType, contract.ContractName); 83 DiscoverGenericParts(unconstrained); 84 85 ICollection<DiscoveredExport> forKey; 86 if (_discoveredParts.TryGetValue(unconstrained, out forKey)) 87 { 88 foreach (var export in forKey) 89 { 90 var subsettedConstraints = contract.MetadataConstraints.Where(c => export.Metadata.ContainsKey(c.Key)).ToDictionary(c => c.Key, c => export.Metadata[c.Key]); 91 if (subsettedConstraints.Count != 0) 92 { 93 var constrainedSubset = new CompositionContract(unconstrained.ContractType, unconstrained.ContractName, subsettedConstraints); 94 95 if (constrainedSubset.Equals(contract)) 96 AddDiscoveredExport(export, contract); 97 } 98 } 99 } 100 } 101 } 102 103 // If the contract is a closed generic, look for open generics 104 // that close it. DiscoverGenericParts(CompositionContract contract)105 private void DiscoverGenericParts(CompositionContract contract) 106 { 107 if (!contract.ContractType.IsConstructedGenericType) 108 return; 109 110 var gtd = contract.ContractType.GetGenericTypeDefinition(); 111 var openGenericContract = contract.ChangeType(gtd); 112 ICollection<DiscoveredExport> openGenericParts; 113 if (!_discoveredParts.TryGetValue(openGenericContract, out openGenericParts)) 114 return; 115 116 var typeArguments = contract.ContractType.GenericTypeArguments; 117 foreach (var open in openGenericParts) 118 { 119 DiscoveredPart closed; 120 if (open.Part.TryCloseGenericPart(typeArguments, out closed)) 121 AddDiscoveredPart(closed); 122 } 123 } 124 CreateActivationFeatures(AttributedModelProvider attributeContext)125 private static ActivationFeature[] CreateActivationFeatures(AttributedModelProvider attributeContext) 126 { 127 return new ActivationFeature[] { 128 new DisposalFeature(), 129 new PropertyInjectionFeature(attributeContext), 130 new OnImportsSatisfiedFeature(attributeContext), 131 new LifetimeFeature(), 132 }; 133 } 134 DebugGetActivationFeatures(AttributedModelProvider attributeContext)135 internal static ActivationFeature[] DebugGetActivationFeatures(AttributedModelProvider attributeContext) 136 { 137 return CreateActivationFeatures(attributeContext); 138 } 139 } 140 } 141