1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
4 using System.Collections.Generic;
5 using System.Composition.Convention;
6 using System.Composition.Debugging;
7 using System.Composition.Hosting.Core;
8 using System.Composition.TypedParts;
9 using System.Composition.TypedParts.Util;
10 using System.Diagnostics;
12 using System.Reflection;
14 namespace System.Composition.Hosting
17 /// Configures and constructs a lightweight container.
19 [DebuggerTypeProxy(typeof(ContainerConfigurationDebuggerProxy))]
20 public class ContainerConfiguration
22 private AttributedModelProvider _defaultAttributeContext;
23 private readonly IList<ExportDescriptorProvider> _addedSources = new List<ExportDescriptorProvider>();
24 private readonly IList<Tuple<IEnumerable<Type>, AttributedModelProvider>> _types = new List<Tuple<IEnumerable<Type>, AttributedModelProvider>>();
27 /// Create the container. The value returned from this method provides
28 /// the exports in the container, as well as a means to dispose the container.
30 /// <returns>The container.</returns>
31 public CompositionHost CreateContainer()
33 var providers = _addedSources.ToList();
35 foreach (var typeSet in _types)
37 var ac = typeSet.Item2 ?? _defaultAttributeContext ?? new DirectAttributeContext();
39 providers.Add(new TypedPartExportDescriptorProvider(typeSet.Item1, ac));
42 return CompositionHost.CreateCompositionHost(providers.ToArray());
46 /// Add an export descriptor provider to the container.
48 /// <param name="exportDescriptorProvider">An export descriptor provider.</param>
49 /// <returns>A configuration object allowing configuration to continue.</returns>
50 public ContainerConfiguration WithProvider(ExportDescriptorProvider exportDescriptorProvider)
52 if (exportDescriptorProvider is null)
54 throw new ArgumentNullException(nameof(exportDescriptorProvider));
57 _addedSources.Add(exportDescriptorProvider);
62 /// Add conventions defined using a <see cref="AttributedModelProvider"/> to the container.
63 /// These will be used as the default conventions; types and assemblies added with a
64 /// specific convention will use their own.
66 /// <param name="conventions"></param>
67 /// <returns>A configuration object allowing configuration to continue.</returns>
68 public ContainerConfiguration WithDefaultConventions(AttributedModelProvider conventions)
70 if (conventions is null)
72 throw new ArgumentNullException(nameof(conventions));
75 if (_defaultAttributeContext != null)
76 throw new InvalidOperationException(SR.ContainerConfiguration_DefaultConventionSet);
78 _defaultAttributeContext = conventions;
83 /// Add a part type to the container. If the part type does not have any exports it
86 /// <param name="partType">The part type.</param>
87 /// <returns>A configuration object allowing configuration to continue.</returns>
88 public ContainerConfiguration WithPart(Type partType)
90 return WithPart(partType, null);
94 /// Add a part type to the container. If the part type does not have any exports it
97 /// <param name="partType">The part type.</param>
98 /// <param name="conventions">Conventions represented by a <see cref="AttributedModelProvider"/>, or null.</param>
99 /// <returns>A configuration object allowing configuration to continue.</returns>
100 public ContainerConfiguration WithPart(Type partType, AttributedModelProvider conventions)
102 if (partType == null) throw new ArgumentNullException(nameof(partType));
103 return WithParts(new[] { partType }, conventions);
107 /// Add a part type to the container. If the part type does not have any exports it
110 /// <typeparam name="TPart">The part type.</typeparam>
111 /// <returns>A configuration object allowing configuration to continue.</returns>
112 public ContainerConfiguration WithPart<TPart>()
114 return WithPart<TPart>(null);
118 /// Add a part type to the container. If the part type does not have any exports it
121 /// <typeparam name="TPart">The part type.</typeparam>
122 /// <param name="conventions">Conventions represented by a <see cref="AttributedModelProvider"/>, or null.</param>
123 /// <returns>A configuration object allowing configuration to continue.</returns>
124 public ContainerConfiguration WithPart<TPart>(AttributedModelProvider conventions)
126 return WithPart(typeof(TPart), conventions);
130 /// Add part types to the container. If a part type does not have any exports it
133 /// <param name="partTypes">The part types.</param>
134 /// <returns>A configuration object allowing configuration to continue.</returns>
135 public ContainerConfiguration WithParts(params Type[] partTypes)
137 return WithParts((IEnumerable<Type>)partTypes);
141 /// Add part types to the container. If a part type does not have any exports it
144 /// <param name="partTypes">The part types.</param>
145 /// <returns>A configuration object allowing configuration to continue.</returns>
146 public ContainerConfiguration WithParts(IEnumerable<Type> partTypes)
148 return WithParts(partTypes, null);
152 /// Add part types to the container. If a part type does not have any exports it
155 /// <param name="partTypes">The part types.</param>
156 /// <param name="conventions">Conventions represented by a <see cref="AttributedModelProvider"/>, or null.</param>
157 /// <returns>A configuration object allowing configuration to continue.</returns>
158 public ContainerConfiguration WithParts(IEnumerable<Type> partTypes, AttributedModelProvider conventions)
160 if (partTypes is null)
162 throw new ArgumentNullException(nameof(partTypes));
165 _types.Add(Tuple.Create(partTypes, conventions));
170 /// Add part types from an assembly to the container. If a part type does not have any exports it
173 /// <param name="assembly">The assembly from which to add part types.</param>
174 /// <returns>A configuration object allowing configuration to continue.</returns>
175 public ContainerConfiguration WithAssembly(Assembly assembly)
177 return WithAssembly(assembly, null);
181 /// Add part types from an assembly to the container. If a part type does not have any exports it
184 /// <param name="assembly">The assembly from which to add part types.</param>
185 /// <param name="conventions">Conventions represented by a <see cref="AttributedModelProvider"/>, or null.</param>
186 /// <returns>A configuration object allowing configuration to continue.</returns>
187 public ContainerConfiguration WithAssembly(Assembly assembly, AttributedModelProvider conventions)
189 return WithAssemblies(new[] { assembly }, conventions);
193 /// Add part types from a list of assemblies to the container. If a part type does not have any exports it
196 /// <param name="assemblies">Assemblies containing part types.</param>
197 /// <returns>A configuration object allowing configuration to continue.</returns>
198 public ContainerConfiguration WithAssemblies(IEnumerable<Assembly> assemblies)
200 return WithAssemblies(assemblies, null);
204 /// Add part types from a list of assemblies to the container. If a part type does not have any exports it
207 /// <param name="assemblies">Assemblies containing part types.</param>
208 /// <param name="conventions">Conventions represented by a <see cref="AttributedModelProvider"/>, or null.</param>
209 /// <returns>A configuration object allowing configuration to continue.</returns>
210 public ContainerConfiguration WithAssemblies(IEnumerable<Assembly> assemblies, AttributedModelProvider conventions)
212 if (assemblies is null)
214 throw new ArgumentNullException(nameof(assemblies));
217 return WithParts(assemblies.SelectMany(a => a.DefinedTypes.Select(dt => dt.AsType())), conventions);
221 /// Add a single instance to the container.
223 /// <typeparam name="TExport">The type of the contract of the instance.</typeparam>
224 /// <param name="exportedInstance">The instance to add to the container.</param>
225 /// <returns>A configuration object allowing configuration to continue.</returns>
226 public ContainerConfiguration WithExport<TExport>(TExport exportedInstance)
228 if (exportedInstance is null)
230 throw new ArgumentNullException(nameof(exportedInstance));
233 return WithExport(exportedInstance, null, null);
237 /// Add a single instance to the container.
239 /// <typeparam name="TExport">The type of the contract of the instance.</typeparam>
240 /// <param name="exportedInstance">The instance to add to the container.</param>
241 /// <param name="contractName">Optionally, a name that discriminates this contract from others with the same type.</param>
242 /// <param name="metadata">Optionally, a non-empty collection of named constraints that apply to the contract.</param>
243 /// <returns>A configuration object allowing configuration to continue.</returns>
244 public ContainerConfiguration WithExport<TExport>(TExport exportedInstance, string contractName = null, IDictionary<string, object> metadata = null)
246 if (exportedInstance is null)
248 throw new ArgumentNullException(nameof(exportedInstance));
251 return WithExport(typeof(TExport), exportedInstance, contractName, metadata);
255 /// Add a single instance to the container.
257 /// <param name="contractType">The type of the contract of the instance.</param>
258 /// <param name="exportedInstance">The instance to add to the container.</param>
259 /// <returns>A configuration object allowing configuration to continue.</returns>
260 public ContainerConfiguration WithExport(Type contractType, object exportedInstance)
262 if (contractType is null)
264 throw new ArgumentNullException(nameof(contractType));
266 if (exportedInstance is null)
268 throw new ArgumentNullException(nameof(exportedInstance));
271 return WithExport(contractType, exportedInstance, null, null);
275 /// Add a single instance to the container.
277 /// <param name="contractType">The type of the contract of the instance.</param>
278 /// <param name="exportedInstance">The instance to add to the container.</param>
279 /// <param name="contractName">Optionally, a name that discriminates this contract from others with the same type.</param>
280 /// <param name="metadata">Optionally, a non-empty collection of named constraints that apply to the contract.</param>
281 /// <returns>A configuration object allowing configuration to continue.</returns>
282 public ContainerConfiguration WithExport(Type contractType, object exportedInstance, string contractName = null, IDictionary<string, object> metadata = null)
284 if (contractType is null)
286 throw new ArgumentNullException(nameof(contractType));
288 if (exportedInstance is null)
290 throw new ArgumentNullException(nameof(exportedInstance));
293 return WithProvider(new InstanceExportDescriptorProvider(exportedInstance, contractType, contractName, metadata));
296 internal ExportDescriptorProvider[] DebugGetAddedExportDescriptorProviders()
298 return _addedSources.ToArray();
301 internal Tuple<IEnumerable<Type>, AttributedModelProvider>[] DebugGetRegisteredTypes()
303 return _types.ToArray();
306 internal AttributedModelProvider DebugGetDefaultAttributeContext()
308 return _defaultAttributeContext;