1 // -----------------------------------------------------------------------
\r
2 // Copyright (c) Microsoft Corporation. All rights reserved.
\r
3 // -----------------------------------------------------------------------
\r
5 using System.Collections.Generic;
\r
6 using System.Collections.ObjectModel;
\r
7 using System.ComponentModel.Composition.Primitives;
\r
8 using System.Diagnostics;
\r
9 using System.Diagnostics.CodeAnalysis;
\r
11 using Microsoft.Internal;
\r
12 using Microsoft.Internal.Collections;
\r
13 using System.Threading;
\r
15 namespace System.ComponentModel.Composition.Hosting
\r
17 public partial class CompositionContainer : ExportProvider, ICompositionService, IDisposable
\r
19 private ImportEngine _importEngine;
\r
20 private ComposablePartExportProvider _partExportProvider;
\r
21 private AggregateExportProvider _aggregatingExportProvider;
\r
22 private ExportProvider _rootProvider;
\r
23 private CatalogExportProvider _catalogExportProvider;
\r
24 private readonly ReadOnlyCollection<ExportProvider> _providers;
\r
25 private volatile int _isDisposed = 0;
\r
28 /// Initializes a new instance of the <see cref="CompositionContainer"/> class.
\r
30 public CompositionContainer()
\r
31 : this((ComposablePartCatalog)null)
\r
36 /// Initializes a new instance of the <see cref="CompositionContainer"/> class
\r
37 /// with the specified export providers.
\r
39 /// <param name="providers">
\r
40 /// A <see cref="Array"/> of <see cref="ExportProvider"/> objects which provide
\r
41 /// the <see cref="CompositionContainer"/> access to <see cref="Export"/> objects,
\r
42 /// or <see langword="null"/> to set <see cref="Providers"/> to an empty
\r
43 /// <see cref="ReadOnlyCollection{T}"/>.
\r
45 /// <exception cref="ArgumentException">
\r
46 /// <paramref name="providers"/> contains an element that is <see langword="null"/>.
\r
48 public CompositionContainer(params ExportProvider[] providers) :
\r
49 this((ComposablePartCatalog)null, providers)
\r
54 /// Initializes a new instance of the <see cref="CompositionContainer"/> class
\r
55 /// with the specified catalog and export providers.
\r
57 /// <param name="providers">
\r
58 /// A <see cref="Array"/> of <see cref="ExportProvider"/> objects which provide
\r
59 /// the <see cref="CompositionContainer"/> access to <see cref="Export"/> objects,
\r
60 /// or <see langword="null"/> to set <see cref="Providers"/> to an empty
\r
61 /// <see cref="ReadOnlyCollection{T}"/>.
\r
63 /// <exception cref="ArgumentException">
\r
64 /// <paramref name="providers"/> contains an element that is <see langword="null"/>.
\r
66 public CompositionContainer(ComposablePartCatalog catalog, params ExportProvider[] providers):
\r
67 this(catalog, false, providers)
\r
71 public CompositionContainer(ComposablePartCatalog catalog, bool isThreadSafe, params ExportProvider[] providers)
\r
73 this._importEngine = new ImportEngine(this, isThreadSafe);
\r
74 this._partExportProvider = new ComposablePartExportProvider(isThreadSafe);
\r
75 this._partExportProvider.SourceProvider = this;
\r
76 this._providers = new ReadOnlyCollection<ExportProvider>(providers != null ? (ExportProvider[])providers.Clone() : new ExportProvider[0]);
\r
78 List<ExportProvider> providerList = new List<ExportProvider>();
\r
80 providerList.Add(this._partExportProvider);
\r
82 if (catalog != null)
\r
84 this._catalogExportProvider = new CatalogExportProvider(catalog, isThreadSafe);
\r
85 this._catalogExportProvider.SourceProvider = this;
\r
87 providerList.Add(this._catalogExportProvider);
\r
90 foreach (var provider in this._providers)
\r
92 if (provider == null)
\r
94 throw ExceptionBuilder.CreateContainsNullElement("providers");
\r
96 providerList.Add(provider);
\r
99 // we only build the aggregating provider if necessary - that is, if we have more than one provider to aggregate
\r
100 if (providerList.Count > 1)
\r
102 this._aggregatingExportProvider = new AggregateExportProvider(providerList);
\r
103 this._rootProvider = this._aggregatingExportProvider;
\r
107 Assumes.IsTrue(providerList.Count == 1);
\r
108 this._rootProvider = providerList[0];
\r
111 this._rootProvider.ExportsChanged += this.OnExportsChangedInternal;
\r
112 this._rootProvider.ExportsChanging += this.OnExportsChangingInternal;
\r
116 /// Gets the catalog which provides the container access to exports produced
\r
117 /// from composable parts.
\r
120 /// The <see cref="ComposablePartCatalog"/> which provides the
\r
121 /// <see cref="CompositionContainer"/> access to exports produced from
\r
122 /// <see cref="ComposablePart"/> objects. The default is <see langword="null"/>.
\r
124 /// <exception cref="ObjectDisposedException">
\r
125 /// The <see cref="CompositionContainer"/> has been disposed of.
\r
127 public ComposablePartCatalog Catalog
\r
133 if (_catalogExportProvider != null)
\r
135 return _catalogExportProvider.Catalog;
\r
143 /// Gets the export providers which provide the container access to additional exports.
\r
146 /// A <see cref="ReadOnlyCollection{T}"/> of <see cref="ExportProvider"/> objects
\r
147 /// which provide the <see cref="CompositionContainer"/> access to additional
\r
148 /// <see cref="Export"/> objects. The default is an empty
\r
149 /// <see cref="ReadOnlyCollection{T}"/>.
\r
151 /// <exception cref="ObjectDisposedException">
\r
152 /// The <see cref="CompositionContainer"/> has been disposed of.
\r
154 public ReadOnlyCollection<ExportProvider> Providers
\r
158 this.ThrowIfDisposed();
\r
160 return this._providers;
\r
165 /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
\r
167 public void Dispose()
\r
169 this.Dispose(true);
\r
170 GC.SuppressFinalize(this);
\r
174 /// Releases unmanaged and - optionally - managed resources
\r
176 /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
\r
177 protected virtual void Dispose(bool disposing)
\r
181 // NOTE : According to http://msdn.microsoft.com/en-us/library/4bw5ewxy.aspx, the warning is bogus when used with Interlocked API.
\r
182 #pragma warning disable 420
\r
183 if (Interlocked.CompareExchange(ref this._isDisposed, 1, 0) == 0)
\r
184 #pragma warning restore 420
\r
186 this._rootProvider.ExportsChanged -= this.OnExportsChangedInternal;
\r
187 this._rootProvider.ExportsChanging -= this.OnExportsChangingInternal;
\r
188 if (this._aggregatingExportProvider != null)
\r
190 this._aggregatingExportProvider.Dispose();
\r
192 this._partExportProvider.Dispose();
\r
193 if (this._catalogExportProvider != null)
\r
195 this._catalogExportProvider.Dispose();
\r
197 this._importEngine.Dispose();
\r
202 public void Compose(CompositionBatch batch)
\r
204 Requires.NotNull(batch, "batch");
\r
206 this.ThrowIfDisposed();
\r
207 this._partExportProvider.Compose(batch);
\r
211 /// Releases the <see cref="Export"/> from the <see cref="CompositionContainer"/>. The behavior
\r
212 /// may vary depending on the implementation of the <see cref="ExportProvider"/> that produced
\r
213 /// the <see cref="Export"/> instance. As a general rule non shared exports should be early
\r
214 /// released causing them to be detached from the container.
\r
216 /// For example the <see cref="CatalogExportProvider"/> will only release
\r
217 /// an <see cref="Export"/> if it comes from a <see cref="ComposablePart"/> that was constructed
\r
218 /// under a <see cref="CreationPolicy.NonShared" /> context. Release in this context means walking
\r
219 /// the dependency chain of the <see cref="Export"/>s, detaching references from the container and
\r
220 /// calling Dispose on the <see cref="ComposablePart"/>s as needed. If the <see cref="Export"/>
\r
221 /// was constructed under a <see cref="CreationPolicy.Shared" /> context the
\r
222 /// <see cref="CatalogExportProvider"/> will do nothing as it may be in use by other requestors.
\r
223 /// Those will only be detached when the container is itself disposed.
\r
225 /// <param name="export"><see cref="Export"/> that needs to be released.</param>
\r
226 /// <exception cref="ArgumentNullException">
\r
227 /// <paramref name="export"/> is <see langword="null"/>.
\r
229 [SuppressMessage("Microsoft.Performance", "CA1822")]
\r
230 public void ReleaseExport(Export export)
\r
232 Requires.NotNull(export, "export");
\r
234 IDisposable dependency = export as IDisposable;
\r
236 if (dependency != null)
\r
238 dependency.Dispose();
\r
243 /// Releases the <see cref="Lazy{T}"/> from the <see cref="CompositionContainer"/>. The behavior
\r
244 /// may vary depending on the implementation of the <see cref="ExportProvider"/> that produced
\r
245 /// the <see cref="Export"/> instance. As a general rule non shared exports should be early
\r
246 /// released causing them to be detached from the container.
\r
248 /// For example the <see cref="CatalogExportProvider"/> will only release
\r
249 /// an <see cref="Lazy{T}"/> if it comes from a <see cref="ComposablePart"/> that was constructed
\r
250 /// under a <see cref="CreationPolicy.NonShared" /> context. Release in this context means walking
\r
251 /// the dependency chain of the <see cref="Export"/>s, detaching references from the container and
\r
252 /// calling Dispose on the <see cref="ComposablePart"/>s as needed. If the <see cref="Export"/>
\r
253 /// was constructed under a <see cref="CreationPolicy.Shared" /> context the
\r
254 /// <see cref="CatalogExportProvider"/> will do nothing as it may be in use by other requestors.
\r
255 /// Those will only be detached when the container is itself disposed.
\r
257 /// <param name="export"><see cref="Export"/> that needs to be released.</param>
\r
258 /// <exception cref="ArgumentNullException">
\r
259 /// <paramref name="export"/> is <see langword="null"/>.
\r
261 [SuppressMessage("Microsoft.Performance", "CA1822")]
\r
262 public void ReleaseExport<T>(Lazy<T> export)
\r
264 Requires.NotNull(export, "export");
\r
266 IDisposable dependency = export as IDisposable;
\r
268 if (dependency != null)
\r
270 dependency.Dispose();
\r
275 /// Releases a set of <see cref="Export"/>s from the <see cref="CompositionContainer"/>.
\r
276 /// See also <see cref="ReleaseExport"/>.
\r
278 /// <param name="exports"><see cref="Export"/>s that need to be released.</param>
\r
279 /// <exception cref="ArgumentNullException">
\r
280 /// <paramref name="exports"/> is <see langword="null"/>.
\r
282 /// <exception cref="ArgumentException">
\r
283 /// <paramref name="exports"/> contains an element that is <see langword="null"/>.
\r
285 public void ReleaseExports(IEnumerable<Export> exports)
\r
287 Requires.NotNullOrNullElements(exports, "exports");
\r
289 foreach (Export export in exports)
\r
291 this.ReleaseExport(export);
\r
296 /// Releases a set of <see cref="Export"/>s from the <see cref="CompositionContainer"/>.
\r
297 /// See also <see cref="ReleaseExport"/>.
\r
299 /// <param name="exports"><see cref="Export"/>s that need to be released.</param>
\r
300 /// <exception cref="ArgumentNullException">
\r
301 /// <paramref name="exports"/> is <see langword="null"/>.
\r
303 /// <exception cref="ArgumentException">
\r
304 /// <paramref name="exports"/> contains an element that is <see langword="null"/>.
\r
306 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
\r
307 public void ReleaseExports<T>(IEnumerable<Lazy<T>> exports)
\r
309 Requires.NotNullOrNullElements(exports, "exports");
\r
311 foreach (Lazy<T> export in exports)
\r
313 this.ReleaseExport(export);
\r
318 /// Releases a set of <see cref="Export"/>s from the <see cref="CompositionContainer"/>.
\r
319 /// See also <see cref="ReleaseExport"/>.
\r
321 /// <param name="exports"><see cref="Export"/>s that need to be released.</param>
\r
322 /// <exception cref="ArgumentNullException">
\r
323 /// <paramref name="exports"/> is <see langword="null"/>.
\r
325 /// <exception cref="ArgumentException">
\r
326 /// <paramref name="exports"/> contains an element that is <see langword="null"/>.
\r
328 [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
\r
329 public void ReleaseExports<T, TMetadataView>(IEnumerable<Lazy<T, TMetadataView>> exports)
\r
331 Requires.NotNullOrNullElements(exports, "exports");
\r
333 foreach (Lazy<T, TMetadataView> export in exports)
\r
335 this.ReleaseExport(export);
\r
340 /// Sets the imports of the specified composable part exactly once and they will not
\r
341 /// ever be recomposed.
\r
343 /// <param name="part">
\r
344 /// The <see cref="ComposablePart"/> to set the imports.
\r
346 /// <exception cref="ArgumentNullException">
\r
347 /// <paramref name="part"/> is <see langword="null"/>.
\r
349 /// <exception cref="CompositionException">
\r
350 /// An error occurred during composition. <see cref="CompositionException.Errors"/> will
\r
351 /// contain a collection of errors that occurred.
\r
353 /// <exception cref="ObjectDisposedException">
\r
354 /// The <see cref="ICompositionService"/> has been disposed of.
\r
356 public void SatisfyImportsOnce(ComposablePart part)
\r
358 this.ThrowIfDisposed();
\r
359 this._importEngine.SatisfyImportsOnce(part);
\r
362 internal void OnExportsChangedInternal(object sender, ExportsChangeEventArgs e)
\r
364 this.OnExportsChanged(e);
\r
367 internal void OnExportsChangingInternal(object sender, ExportsChangeEventArgs e)
\r
369 this.OnExportsChanging(e);
\r
373 /// Returns all exports that match the conditions of the specified import.
\r
375 /// <param name="definition">The <see cref="ImportDefinition"/> that defines the conditions of the
\r
376 /// <see cref="Export"/> to get.</param>
\r
377 /// <returns></returns>
\r
379 /// An <see cref="IEnumerable{T}"/> of <see cref="Export"/> objects that match
\r
380 /// the conditions defined by <see cref="ImportDefinition"/>, if found; otherwise, an
\r
381 /// empty <see cref="IEnumerable{T}"/>.
\r
384 /// <note type="inheritinfo">
\r
385 /// The implementers should not treat the cardinality-related mismatches as errors, and are not
\r
386 /// expected to throw exceptions in those cases.
\r
387 /// For instance, if the import requests exactly one export and the provider has no matching exports or more than one,
\r
388 /// it should return an empty <see cref="IEnumerable{T}"/> of <see cref="Export"/>.
\r
391 protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
\r
393 this.ThrowIfDisposed();
\r
395 IEnumerable<Export> exports = null;
\r
396 this._rootProvider.TryGetExports(definition, atomicComposition, out exports);
\r
401 [DebuggerStepThrough]
\r
402 private void ThrowIfDisposed()
\r
404 if (this._isDisposed == 1)
\r
406 throw ExceptionBuilder.CreateObjectDisposed(this);
\r