1 // -----------------------------------------------------------------------
\r
2 // Copyright (c) Microsoft Corporation. All rights reserved.
\r
3 // -----------------------------------------------------------------------
\r
5 using System.Collections.Generic;
\r
6 using System.ComponentModel.Composition.Hosting;
\r
8 using System.Linq.Expressions;
\r
9 using Microsoft.Internal;
\r
11 namespace System.ComponentModel.Composition.Primitives
\r
14 /// Represents a contract name and metadata-based import
\r
15 /// required by a <see cref="ComposablePart"/> object.
\r
17 public class ContractBasedImportDefinition : ImportDefinition
\r
19 // Unlike contract name, required metadata has a sensible default; set it to an empty
\r
20 // enumerable, so that derived definitions only need to override ContractName by default.
\r
21 private readonly IEnumerable<KeyValuePair<string, Type>> _requiredMetadata = Enumerable.Empty<KeyValuePair<string, Type>>();
\r
22 private Expression<Func<ExportDefinition, bool>> _constraint;
\r
23 private readonly CreationPolicy _requiredCreationPolicy = CreationPolicy.Any;
\r
24 private readonly string _requiredTypeIdentity = null;
\r
27 /// Initializes a new instance of the <see cref="ContractBasedImportDefinition"/> class.
\r
30 /// <note type="inheritinfo">
\r
31 /// Derived types calling this constructor can optionally override the
\r
32 /// <see cref="ImportDefinition.ContractName"/>, <see cref="RequiredTypeIdentity"/>,
\r
33 /// <see cref="RequiredMetadata"/>, <see cref="ImportDefinition.Cardinality"/>,
\r
34 /// <see cref="ImportDefinition.IsPrerequisite"/>, <see cref="ImportDefinition.IsRecomposable"/>
\r
35 /// and <see cref="RequiredCreationPolicy"/> properties.
\r
38 protected ContractBasedImportDefinition()
\r
43 /// Initializes a new instance of the <see cref="ContractBasedImportDefinition"/> class
\r
44 /// with the specified contract name, required metadataq, cardinality, value indicating
\r
45 /// if the import definition is recomposable and a value indicating if the import definition
\r
46 /// is a prerequisite.
\r
48 /// <param name="contractName">
\r
49 /// A <see cref="String"/> containing the contract name of the
\r
50 /// <see cref="Export"/> required by the <see cref="ContractBasedImportDefinition"/>.
\r
52 /// <param name="requiredTypeIdentity">
\r
53 /// The type identity of the export type expected. Use <see cref="AttributedModelServices.GetTypeIdentity(Type)"/>
\r
54 /// to generate a type identity for a given type. If no specific type is required pass <see langword="null"/>.
\r
56 /// <param name="requiredMetadata">
\r
57 /// An <see cref="IEnumerable{T}"/> of <see cref="String"/> objects containing
\r
58 /// the metadata names of the <see cref="Export"/> required by the
\r
59 /// <see cref="ContractBasedImportDefinition"/>; or <see langword="null"/> to
\r
60 /// set the <see cref="RequiredMetadata"/> property to an empty <see cref="IEnumerable{T}"/>.
\r
62 /// <param name="cardinality">
\r
63 /// One of the <see cref="ImportCardinality"/> values indicating the
\r
64 /// cardinality of the <see cref="Export"/> objects required by the
\r
65 /// <see cref="ContractBasedImportDefinition"/>.
\r
67 /// <param name="isRecomposable">
\r
68 /// <see langword="true"/> if the <see cref="ContractBasedImportDefinition"/> can be satisfied
\r
69 /// multiple times throughout the lifetime of a <see cref="ComposablePart"/>, otherwise,
\r
70 /// <see langword="false"/>.
\r
72 /// <param name="isPrerequisite">
\r
73 /// <see langword="true"/> if the <see cref="ContractBasedImportDefinition"/> is required to be
\r
74 /// satisfied before a <see cref="ComposablePart"/> can start producing exported
\r
75 /// objects; otherwise, <see langword="false"/>.
\r
77 /// <param name="requiredCreationPolicy">
\r
78 /// A value indicating that the importer requires a specific <see cref="CreationPolicy"/> for
\r
79 /// the exports used to satisfy this import. If no specific <see cref="CreationPolicy"/> is needed
\r
80 /// pass the default <see cref="CreationPolicy.Any"/>.
\r
82 /// <exception cref="ArgumentNullException">
\r
83 /// <paramref name="contractName"/> is <see langword="null"/>.
\r
85 /// <exception cref="ArgumentException">
\r
86 /// <paramref name="contractName"/> is an empty string ("").
\r
90 /// <paramref name="requiredMetadata"/> contains an element that is <see langword="null"/>.
\r
94 /// <paramref name="cardinality"/> is not one of the <see cref="ImportCardinality"/>
\r
97 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
\r
98 public ContractBasedImportDefinition(string contractName, string requiredTypeIdentity, IEnumerable<KeyValuePair<string, Type>> requiredMetadata,
\r
99 ImportCardinality cardinality, bool isRecomposable, bool isPrerequisite, CreationPolicy requiredCreationPolicy)
\r
100 : base(contractName, cardinality, isRecomposable, isPrerequisite)
\r
102 Requires.NotNullOrEmpty(contractName, "contractName");
\r
103 Requires.NullOrNotNullElements(requiredMetadata, "requiredMetadata");
\r
105 this._requiredTypeIdentity = requiredTypeIdentity;
\r
107 if (requiredMetadata != null)
\r
109 this._requiredMetadata = requiredMetadata;
\r
112 this._requiredCreationPolicy = requiredCreationPolicy;
\r
116 /// The type identity of the export type expected.
\r
119 /// A <see cref="string"/> that is generated by <see cref="AttributedModelServices.GetTypeIdentity(Type)"/>
\r
120 /// on the type that this import expects. If the value is <see langword="null"/> then this import
\r
121 /// doesn't expect a particular type.
\r
123 public virtual string RequiredTypeIdentity
\r
125 get { return this._requiredTypeIdentity; }
\r
129 /// Gets the metadata names of the export required by the import definition.
\r
132 /// An <see cref="IEnumerable{T}"/> of pairs of metadata keys and types of the <see cref="Export"/> required by the
\r
133 /// <see cref="ContractBasedImportDefinition"/>. The default is an empty
\r
134 /// <see cref="IEnumerable{T}"/>.
\r
137 /// <note type="inheritinfo">
\r
138 /// Overriders of this property should never return <see langword="null"/>
\r
139 /// or return an <see cref="IEnumerable{T}"/> that contains an element that is
\r
140 /// <see langword="null"/>. If the definition does not contain required metadata,
\r
141 /// return an empty <see cref="IEnumerable{T}"/> instead.
\r
144 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
\r
145 public virtual IEnumerable<KeyValuePair<string, Type>> RequiredMetadata
\r
147 get { return this._requiredMetadata; }
\r
151 /// Gets or sets a value indicating that the importer requires a specific
\r
152 /// <see cref="CreationPolicy"/> for the exports used to satisfy this import. T
\r
155 /// <see cref="CreationPolicy.Any"/> - default value, used if the importer doesn't
\r
156 /// require a specific <see cref="CreationPolicy"/>.
\r
158 /// <see cref="CreationPolicy.Shared"/> - Requires that all exports used should be shared
\r
159 /// by everyone in the container.
\r
161 /// <see cref="CreationPolicy.NonShared"/> - Requires that all exports used should be
\r
162 /// non-shared in a container and thus everyone gets their own instance.
\r
164 public virtual CreationPolicy RequiredCreationPolicy
\r
166 get { return this._requiredCreationPolicy; }
\r
170 /// Gets an expression that defines conditions that must be matched for the import
\r
171 /// described by the import definition to be satisfied.
\r
174 /// A <see cref="Expression{TDelegate}"/> containing a <see cref="Func{T, TResult}"/>
\r
175 /// that defines the conditions that must be matched for the
\r
176 /// <see cref="ImportDefinition"/> to be satisfied by an <see cref="Export"/>.
\r
180 /// This property returns an expression that defines conditions based on the
\r
181 /// <see cref="ImportDefinition.ContractName"/>, <see cref="RequiredTypeIdentity"/>,
\r
182 /// <see cref="RequiredMetadata"/>, and <see cref="RequiredCreationPolicy"/>
\r
186 public override Expression<Func<ExportDefinition, bool>> Constraint
\r
190 if (this._constraint == null)
\r
192 this._constraint = ConstraintServices.CreateConstraint(this.ContractName, this.RequiredTypeIdentity, this.RequiredMetadata, this.RequiredCreationPolicy);
\r
195 return this._constraint;
\r
200 /// Executes an optimized version of the contraint given by the <see cref="Constraint"/> property
\r
202 /// <param name="exportDefinition">
\r
203 /// A definition for a <see cref="Export"/> used to determine if it satisfies the
\r
204 /// requirements for this <see cref="ImportDefinition"/>.
\r
207 /// <see langword="True"/> if the <see cref="Export"/> satisfies the requirements for
\r
208 /// this <see cref="ImportDefinition"/>, otherwise returns <see langword="False"/>.
\r
211 /// <note type="inheritinfo">
\r
212 /// Overrides of this method can provide a more optimized execution of the
\r
213 /// <see cref="Constraint"/> property but the result should remain consistent.
\r
216 public override bool IsConstraintSatisfiedBy(ExportDefinition exportDefinition)
\r
218 if (!StringComparers.ContractName.Equals(this.ContractName, exportDefinition.ContractName))
\r
223 return MatchRequiredMatadata(exportDefinition);
\r
226 private bool MatchRequiredMatadata(ExportDefinition definition)
\r
228 if (!string.IsNullOrEmpty(this.RequiredTypeIdentity))
\r
230 string exportTypeIdentity = definition.Metadata.GetValue<string>(CompositionConstants.ExportTypeIdentityMetadataName);
\r
232 if (!StringComparers.ContractName.Equals(this.RequiredTypeIdentity, exportTypeIdentity))
\r
238 foreach (KeyValuePair<string, Type> metadataItem in this.RequiredMetadata)
\r
240 string metadataKey = metadataItem.Key;
\r
241 Type metadataValueType = metadataItem.Value;
\r
243 object metadataValue = null;
\r
244 if (!definition.Metadata.TryGetValue(metadataKey, out metadataValue))
\r
249 if (metadataValue != null)
\r
251 // the metadata value is not null, we can rely on IsInstanceOfType to do the right thing
\r
252 if (!metadataValueType.IsInstanceOfType(metadataValue))
\r
259 // this is an unfortunate special case - typeof(object).IsInstanceofType(null) == false
\r
260 // basically nulls are not considered valid values for anything
\r
261 // We want them to match anything that is a reference type
\r
262 if (metadataValueType.IsValueType)
\r
264 // this is a pretty expensive check, but we only invoke it when metadata values are null, which is very rare
\r
270 if (this.RequiredCreationPolicy == CreationPolicy.Any)
\r
275 CreationPolicy exportPolicy = definition.Metadata.GetValue<CreationPolicy>(CompositionConstants.PartCreationPolicyMetadataName);
\r
276 return exportPolicy == CreationPolicy.Any ||
\r
277 exportPolicy == this.RequiredCreationPolicy;
\r