b7efdca28c31dd36ac2c01614589a39201d34a5c
[platform/upstream/dotnet/runtime.git] /
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
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;
11 using System.Linq;
12 using System.Reflection;
13
14 namespace System.Composition.Hosting
15 {
16     /// <summary>
17     /// Configures and constructs a lightweight container.
18     /// </summary>
19     [DebuggerTypeProxy(typeof(ContainerConfigurationDebuggerProxy))]
20     public class ContainerConfiguration
21     {
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>>();
25
26         /// <summary>
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.
29         /// </summary>
30         /// <returns>The container.</returns>
31         public CompositionHost CreateContainer()
32         {
33             var providers = _addedSources.ToList();
34
35             foreach (var typeSet in _types)
36             {
37                 var ac = typeSet.Item2 ?? _defaultAttributeContext ?? new DirectAttributeContext();
38
39                 providers.Add(new TypedPartExportDescriptorProvider(typeSet.Item1, ac));
40             }
41
42             return CompositionHost.CreateCompositionHost(providers.ToArray());
43         }
44
45         /// <summary>
46         /// Add an export descriptor provider to the container.
47         /// </summary>
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)
51         {
52             if (exportDescriptorProvider is null)
53             {
54                 throw new ArgumentNullException(nameof(exportDescriptorProvider));
55             }
56
57             _addedSources.Add(exportDescriptorProvider);
58             return this;
59         }
60
61         /// <summary>
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.
65         /// </summary>
66         /// <param name="conventions"></param>
67         /// <returns>A configuration object allowing configuration to continue.</returns>
68         public ContainerConfiguration WithDefaultConventions(AttributedModelProvider conventions)
69         {
70             if (conventions is null)
71             {
72                 throw new ArgumentNullException(nameof(conventions));
73             }
74
75             if (_defaultAttributeContext != null)
76                 throw new InvalidOperationException(SR.ContainerConfiguration_DefaultConventionSet);
77
78             _defaultAttributeContext = conventions;
79             return this;
80         }
81
82         /// <summary>
83         /// Add a part type to the container. If the part type does not have any exports it
84         /// will be ignored.
85         /// </summary>
86         /// <param name="partType">The part type.</param>
87         /// <returns>A configuration object allowing configuration to continue.</returns>
88         public ContainerConfiguration WithPart(Type partType)
89         {
90             return WithPart(partType, null);
91         }
92
93         /// <summary>
94         /// Add a part type to the container. If the part type does not have any exports it
95         /// will be ignored.
96         /// </summary>
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)
101         {
102             if (partType == null) throw new ArgumentNullException(nameof(partType));
103             return WithParts(new[] { partType }, conventions);
104         }
105
106         /// <summary>
107         /// Add a part type to the container. If the part type does not have any exports it
108         /// will be ignored.
109         /// </summary>
110         /// <typeparam name="TPart">The part type.</typeparam>
111         /// <returns>A configuration object allowing configuration to continue.</returns>
112         public ContainerConfiguration WithPart<TPart>()
113         {
114             return WithPart<TPart>(null);
115         }
116
117         /// <summary>
118         /// Add a part type to the container. If the part type does not have any exports it
119         /// will be ignored.
120         /// </summary>
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)
125         {
126             return WithPart(typeof(TPart), conventions);
127         }
128
129         /// <summary>
130         /// Add part types to the container. If a part type does not have any exports it
131         /// will be ignored.
132         /// </summary>
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)
136         {
137             return WithParts((IEnumerable<Type>)partTypes);
138         }
139
140         /// <summary>
141         /// Add part types to the container. If a part type does not have any exports it
142         /// will be ignored.
143         /// </summary>
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)
147         {
148             return WithParts(partTypes, null);
149         }
150
151         /// <summary>
152         /// Add part types to the container. If a part type does not have any exports it
153         /// will be ignored.
154         /// </summary>
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)
159         {
160             if (partTypes is null)
161             {
162                 throw new ArgumentNullException(nameof(partTypes));
163             }
164
165             _types.Add(Tuple.Create(partTypes, conventions));
166             return this;
167         }
168
169         /// <summary>
170         /// Add part types from an assembly to the container. If a part type does not have any exports it
171         /// will be ignored.
172         /// </summary>
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)
176         {
177             return WithAssembly(assembly, null);
178         }
179
180         /// <summary>
181         /// Add part types from an assembly to the container. If a part type does not have any exports it
182         /// will be ignored.
183         /// </summary>
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)
188         {
189             return WithAssemblies(new[] { assembly }, conventions);
190         }
191
192         /// <summary>
193         /// Add part types from a list of assemblies to the container. If a part type does not have any exports it
194         /// will be ignored.
195         /// </summary>
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)
199         {
200             return WithAssemblies(assemblies, null);
201         }
202
203         /// <summary>
204         /// Add part types from a list of assemblies to the container. If a part type does not have any exports it
205         /// will be ignored.
206         /// </summary>
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)
211         {
212             if (assemblies is null)
213             {
214                 throw new ArgumentNullException(nameof(assemblies));
215             }
216
217             return WithParts(assemblies.SelectMany(a => a.DefinedTypes.Select(dt => dt.AsType())), conventions);
218         }
219
220         /// <summary>
221         /// Add a single instance to the container.
222         /// </summary>
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)
227         {
228             if (exportedInstance is null)
229             {
230                 throw new ArgumentNullException(nameof(exportedInstance));
231             }
232
233             return WithExport(exportedInstance, null, null);
234         }
235
236         /// <summary>
237         /// Add a single instance to the container.
238         /// </summary>
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)
245         {
246             if (exportedInstance is null)
247             {
248                 throw new ArgumentNullException(nameof(exportedInstance));
249             }
250
251             return WithExport(typeof(TExport), exportedInstance, contractName, metadata);
252         }
253
254         /// <summary>
255         /// Add a single instance to the container.
256         /// </summary>
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)
261         {
262             if (contractType is null)
263             {
264                 throw new ArgumentNullException(nameof(contractType));
265             }
266             if (exportedInstance is null)
267             {
268                 throw new ArgumentNullException(nameof(exportedInstance));
269             }
270
271             return WithExport(contractType, exportedInstance, null, null);
272         }
273
274         /// <summary>
275         /// Add a single instance to the container.
276         /// </summary>
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)
283         {
284             if (contractType is null)
285             {
286                 throw new ArgumentNullException(nameof(contractType));
287             }
288             if (exportedInstance is null)
289             {
290                 throw new ArgumentNullException(nameof(exportedInstance));
291             }
292
293             return WithProvider(new InstanceExportDescriptorProvider(exportedInstance, contractType, contractName, metadata));
294         }
295
296         internal ExportDescriptorProvider[] DebugGetAddedExportDescriptorProviders()
297         {
298             return _addedSources.ToArray();
299         }
300
301         internal Tuple<IEnumerable<Type>, AttributedModelProvider>[] DebugGetRegisteredTypes()
302         {
303             return _types.ToArray();
304         }
305
306         internal AttributedModelProvider DebugGetDefaultAttributeContext()
307         {
308             return _defaultAttributeContext;
309         }
310     }
311 }