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.Debugging;
8 using System.Composition.Hosting.Core;
9 using System.Composition.TypedParts;
10 using System.Composition.TypedParts.Util;
11 using System.Diagnostics;
12 using System.Linq;
13 using System.Reflection;
14 
15 namespace System.Composition.Hosting
16 {
17     /// <summary>
18     /// Configures and constructs a lightweight container.
19     /// </summary>
20     [DebuggerTypeProxy(typeof(ContainerConfigurationDebuggerProxy))]
21     public class ContainerConfiguration
22     {
23         private AttributedModelProvider _defaultAttributeContext;
24         private readonly IList<ExportDescriptorProvider> _addedSources = new List<ExportDescriptorProvider>();
25         private readonly IList<Tuple<IEnumerable<Type>, AttributedModelProvider>> _types = new List<Tuple<IEnumerable<Type>, AttributedModelProvider>>();
26 
27         /// <summary>
28         /// Create the container. The value returned from this method provides
29         /// the exports in the container, as well as a means to dispose the container.
30         /// </summary>
31         /// <returns>The container.</returns>
CreateContainer()32         public CompositionHost CreateContainer()
33         {
34             var providers = _addedSources.ToList();
35 
36             foreach (var typeSet in _types)
37             {
38                 var ac = typeSet.Item2 ?? _defaultAttributeContext ?? new DirectAttributeContext();
39 
40                 providers.Add(new TypedPartExportDescriptorProvider(typeSet.Item1, ac));
41             }
42 
43             return CompositionHost.CreateCompositionHost(providers.ToArray());
44         }
45 
46         /// <summary>
47         /// Add an export descriptor provider to the container.
48         /// </summary>
49         /// <param name="exportDescriptorProvider">An export descriptor provider.</param>
50         /// <returns>A configuration object allowing configuration to continue.</returns>
WithProvider(ExportDescriptorProvider exportDescriptorProvider)51         public ContainerConfiguration WithProvider(ExportDescriptorProvider exportDescriptorProvider)
52         {
53             if (exportDescriptorProvider == null) throw new ArgumentNullException(nameof(exportDescriptorProvider));
54             _addedSources.Add(exportDescriptorProvider);
55             return this;
56         }
57 
58         /// <summary>
59         /// Add conventions defined using a <see cref="AttributedModelProvider"/> to the container.
60         /// These will be used as the default conventions; types and assemblies added with a
61         /// specific convention will use their own.
62         /// </summary>
63         /// <param name="conventions"></param>
64         /// <returns>A configuration object allowing configuration to continue.</returns>
WithDefaultConventions(AttributedModelProvider conventions)65         public ContainerConfiguration WithDefaultConventions(AttributedModelProvider conventions)
66         {
67             if (conventions == null) throw new ArgumentNullException(nameof(conventions));
68 
69             if (_defaultAttributeContext != null)
70                 throw new InvalidOperationException(SR.ContainerConfiguration_DefaultConventionSet);
71 
72             _defaultAttributeContext = conventions;
73             return this;
74         }
75 
76         /// <summary>
77         /// Add a part type to the container. If the part type does not have any exports it
78         /// will be ignored.
79         /// </summary>
80         /// <param name="partType">The part type.</param>
81         /// <returns>A configuration object allowing configuration to continue.</returns>
WithPart(Type partType)82         public ContainerConfiguration WithPart(Type partType)
83         {
84             return WithPart(partType, null);
85         }
86 
87         /// <summary>
88         /// Add a part type to the container. If the part type does not have any exports it
89         /// will be ignored.
90         /// </summary>
91         /// <param name="partType">The part type.</param>
92         /// <param name="conventions">Conventions represented by a <see cref="AttributedModelProvider"/>, or null.</param>
93         /// <returns>A configuration object allowing configuration to continue.</returns>
WithPart(Type partType, AttributedModelProvider conventions)94         public ContainerConfiguration WithPart(Type partType, AttributedModelProvider conventions)
95         {
96             if (partType == null) throw new ArgumentNullException(nameof(partType));
97             return WithParts(new[] { partType }, conventions);
98         }
99 
100         /// <summary>
101         /// Add a part type to the container. If the part type does not have any exports it
102         /// will be ignored.
103         /// </summary>
104         /// <typeparam name="TPart">The part type.</typeparam>
105         /// <returns>A configuration object allowing configuration to continue.</returns>
WithPart()106         public ContainerConfiguration WithPart<TPart>()
107         {
108             return WithPart<TPart>(null);
109         }
110 
111         /// <summary>
112         /// Add a part type to the container. If the part type does not have any exports it
113         /// will be ignored.
114         /// </summary>
115         /// <typeparam name="TPart">The part type.</typeparam>
116         /// <param name="conventions">Conventions represented by a <see cref="AttributedModelProvider"/>, or null.</param>
117         /// <returns>A configuration object allowing configuration to continue.</returns>
WithPart(AttributedModelProvider conventions)118         public ContainerConfiguration WithPart<TPart>(AttributedModelProvider conventions)
119         {
120             return WithPart(typeof(TPart), conventions);
121         }
122 
123         /// <summary>
124         /// Add part types to the container. If a part type does not have any exports it
125         /// will be ignored.
126         /// </summary>
127         /// <param name="partTypes">The part types.</param>
128         /// <returns>A configuration object allowing configuration to continue.</returns>
WithParts(params Type[] partTypes)129         public ContainerConfiguration WithParts(params Type[] partTypes)
130         {
131             return WithParts((IEnumerable<Type>)partTypes);
132         }
133 
134         /// <summary>
135         /// Add part types to the container. If a part type does not have any exports it
136         /// will be ignored.
137         /// </summary>
138         /// <param name="partTypes">The part types.</param>
139         /// <returns>A configuration object allowing configuration to continue.</returns>
WithParts(IEnumerable<Type> partTypes)140         public ContainerConfiguration WithParts(IEnumerable<Type> partTypes)
141         {
142             return WithParts(partTypes, null);
143         }
144 
145         /// <summary>
146         /// Add part types to the container. If a part type does not have any exports it
147         /// will be ignored.
148         /// </summary>
149         /// <param name="partTypes">The part types.</param>
150         /// <param name="conventions">Conventions represented by a <see cref="AttributedModelProvider"/>, or null.</param>
151         /// <returns>A configuration object allowing configuration to continue.</returns>
WithParts(IEnumerable<Type> partTypes, AttributedModelProvider conventions)152         public ContainerConfiguration WithParts(IEnumerable<Type> partTypes, AttributedModelProvider conventions)
153         {
154             if (partTypes == null) throw new ArgumentNullException(nameof(partTypes));
155             _types.Add(Tuple.Create(partTypes, conventions));
156             return this;
157         }
158 
159         /// <summary>
160         /// Add part types from an assembly to the container. If a part type does not have any exports it
161         /// will be ignored.
162         /// </summary>
163         /// <param name="assembly">The assembly from which to add part types.</param>
164         /// <returns>A configuration object allowing configuration to continue.</returns>
WithAssembly(Assembly assembly)165         public ContainerConfiguration WithAssembly(Assembly assembly)
166         {
167             return WithAssembly(assembly, null);
168         }
169 
170         /// <summary>
171         /// Add part types from an assembly to the container. If a part type does not have any exports it
172         /// will be ignored.
173         /// </summary>
174         /// <param name="assembly">The assembly from which to add part types.</param>
175         /// <param name="conventions">Conventions represented by a <see cref="AttributedModelProvider"/>, or null.</param>
176         /// <returns>A configuration object allowing configuration to continue.</returns>
WithAssembly(Assembly assembly, AttributedModelProvider conventions)177         public ContainerConfiguration WithAssembly(Assembly assembly, AttributedModelProvider conventions)
178         {
179             return WithAssemblies(new[] { assembly }, conventions);
180         }
181 
182         /// <summary>
183         /// Add part types from a list of assemblies to the container. If a part type does not have any exports it
184         /// will be ignored.
185         /// </summary>
186         /// <param name="assemblies">Assemblies containing part types.</param>
187         /// <returns>A configuration object allowing configuration to continue.</returns>
WithAssemblies(IEnumerable<Assembly> assemblies)188         public ContainerConfiguration WithAssemblies(IEnumerable<Assembly> assemblies)
189         {
190             return WithAssemblies(assemblies, null);
191         }
192 
193         /// <summary>
194         /// Add part types from a list of assemblies to the container. If a part type does not have any exports it
195         /// will be ignored.
196         /// </summary>
197         /// <param name="assemblies">Assemblies containing part types.</param>
198         /// <param name="conventions">Conventions represented by a <see cref="AttributedModelProvider"/>, or null.</param>
199         /// <returns>A configuration object allowing configuration to continue.</returns>
WithAssemblies(IEnumerable<Assembly> assemblies, AttributedModelProvider conventions)200         public ContainerConfiguration WithAssemblies(IEnumerable<Assembly> assemblies, AttributedModelProvider conventions)
201         {
202             if (assemblies == null) throw new ArgumentNullException(nameof(assemblies));
203             return WithParts(assemblies.SelectMany(a => a.DefinedTypes.Select(dt => dt.AsType())), conventions);
204         }
205 
DebugGetAddedExportDescriptorProviders()206         internal ExportDescriptorProvider[] DebugGetAddedExportDescriptorProviders()
207         {
208             return _addedSources.ToArray();
209         }
210 
DebugGetRegisteredTypes()211         internal Tuple<IEnumerable<Type>, AttributedModelProvider>[] DebugGetRegisteredTypes()
212         {
213             return _types.ToArray();
214         }
215 
DebugGetDefaultAttributeContext()216         internal AttributedModelProvider DebugGetDefaultAttributeContext()
217         {
218             return _defaultAttributeContext;
219         }
220     }
221 }
222