update MEF to preview 9
[mcs.git] / class / System.ComponentModel.Composition / src / ComponentModel / System / ComponentModel / Composition / Hosting / AggregateExportProvider.cs
blob62bfe70ce793e9163494279f04b685b13a6f621f
1 // -----------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 // -----------------------------------------------------------------------
4 using System;
5 using System.Collections.Generic;
6 using System.Collections.ObjectModel;
7 using System.ComponentModel.Composition.Primitives;
8 using System.Linq;
9 using Microsoft.Internal;
10 using Microsoft.Internal.Collections;
11 using System.Threading;
13 namespace System.ComponentModel.Composition.Hosting
15 public class AggregateExportProvider : ExportProvider , IDisposable
17 private ReadOnlyCollection<ExportProvider> _providers;
18 private volatile int _isDisposed = 0;
20 /// <summary>
21 /// Initializes a new instance of the <see cref="AggregateExportProvider"/> class.
22 /// </summary>
23 /// <param name="providers">The prioritized list of export providers.</param>
24 /// <remarks>
25 /// <para>
26 /// The <see cref="AggregateExportProvider"/> will consult the providers in the order they have been specfied when
27 /// executing <see cref="ExportProvider.GetExports(ImportDefinition,AtomicComposition)"/>.
28 /// </para>
29 /// <para>
30 /// The <see cref="AggregateExportProvider"/> does not take ownership of the specified providers.
31 /// That is, it will not try to dispose of any of them when it gets disposed.
32 /// </para>
33 /// </remarks>
34 public AggregateExportProvider(params ExportProvider[] providers)
35 : this(providers.AsEnumerable())
39 /// <summary>
40 /// Initializes a new instance of the <see cref="AggregateExportProvider"/> class.
41 /// </summary>
42 /// <param name="providers">The prioritized list of export providers. The providers are consulted in order in which they are supplied.</param>
43 /// <remarks>
44 /// <para>
45 /// The <see cref="AggregateExportProvider"/> will consult the providers in the order they have been specfied when
46 /// executing <see cref="ExportProvider.GetExports(ImportDefinition,AtomicComposition)"/>.
47 /// </para>
48 /// <para>
49 /// The <see cref="AggregateExportProvider"/> does not take ownership of the specified providers.
50 /// That is, it will not try to dispose of any of them when it gets disposed.
51 /// </para>
52 /// </remarks>
53 public AggregateExportProvider(IEnumerable<ExportProvider> providers)
55 List<ExportProvider> providerList = new List<ExportProvider>();
57 if (providers != null)
59 // we are in the constructor, so there's no need to lock anything
60 foreach (var provider in providers)
62 if (provider == null)
64 throw ExceptionBuilder.CreateContainsNullElement("providers");
67 providerList.Add(provider);
69 provider.ExportsChanged += this.OnExportChangedInternal;
70 provider.ExportsChanging += this.OnExportChangingInternal;
74 // this will always fully copy the array
75 this._providers = new ReadOnlyCollection<ExportProvider>(providerList);
78 /// <summary>
79 /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
80 /// </summary>
81 public void Dispose()
83 this.Dispose(true);
84 GC.SuppressFinalize(this);
87 /// <summary>
88 /// Releases unmanaged and - optionally - managed resources
89 /// </summary>
90 /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
91 protected virtual void Dispose(bool disposing)
93 if (disposing)
95 // NOTE : According to http://msdn.microsoft.com/en-us/library/4bw5ewxy.aspx, the warning is bogus when used with Interlocked API.
96 #pragma warning disable 420
97 if (Interlocked.CompareExchange(ref this._isDisposed, 1, 0) == 0)
98 #pragma warning restore 420
100 this._providers.ForEach(provider =>
102 provider.ExportsChanged -= this.OnExportChangedInternal;
103 provider.ExportsChanging -= this.OnExportChangingInternal;
109 /// <summary>
110 /// Gets the export providers which the aggregate export provider aggregates.
111 /// </summary>
112 /// <value>
113 /// A <see cref="ReadOnlyCollection{T}"/> of <see cref="ExportProvider"/> objects
114 /// which the <see cref="AggregateExportProvider"/> aggregates.
115 /// </value>
116 /// <exception cref="ObjectDisposedException">
117 /// The <see cref="AggregateExportProvider"/> has been disposed of.
118 /// </exception>
119 public ReadOnlyCollection<ExportProvider> Providers
123 this.ThrowIfDisposed();
125 return this._providers;
129 /// <summary>
130 /// Returns all exports that match the conditions of the specified import.
131 /// </summary>
132 /// <param name="definition">The <see cref="ImportDefinition"/> that defines the conditions of the
133 /// <see cref="Export"/> to get.</param>
134 /// <returns></returns>
135 /// <result>
136 /// An <see cref="IEnumerable{T}"/> of <see cref="Export"/> objects that match
137 /// the conditions defined by <see cref="ImportDefinition"/>, if found; otherwise, an
138 /// empty <see cref="IEnumerable{T}"/>.
139 /// </result>
140 /// <remarks>
141 /// <note type="inheritinfo">
142 /// The implementers should not treat the cardinality-related mismatches as errors, and are not
143 /// expected to throw exceptions in those cases.
144 /// For instance, if the import requests exactly one export and the provider has no matching exports or more than one,
145 /// it should return an empty <see cref="IEnumerable{T}"/> of <see cref="Export"/>.
146 /// </note>
147 /// </remarks>
148 protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition)
150 this.ThrowIfDisposed();
152 if (definition.Cardinality == ImportCardinality.ZeroOrMore)
154 var exports = new List<Export>();
155 foreach (var provider in this._providers)
157 foreach (var export in provider.GetExports(definition, atomicComposition))
159 exports.Add(export);
162 return exports;
164 else
166 IEnumerable<Export> allExports = Enumerable.Empty<Export>();
168 // if asked for "one or less", the prioriry is at play - the first provider that agrees to return the value
169 // which best complies with the request, wins.
170 foreach (ExportProvider provider in this._providers)
172 IEnumerable<Export> exports;
173 bool cardinalityCheckResult = provider.TryGetExports(definition, atomicComposition, out exports);
174 bool anyExports = exports.FastAny();
175 if (cardinalityCheckResult && anyExports)
177 // NOTE : if the provider returned nothing, we need to proceed, even if it indicated that the
178 // cardinality is correct - when asked for "one or less", the provider might - correctly -
179 // return an empty sequence, but we shouldn't be satisfied with that as providers down the list
180 // might have a value we are interested in.
181 return exports;
183 else
185 // TODO
186 // This is a sneaky thing that we do - if in the end no provider returns the exports with the right cardinality
187 // we simply return the aggregation of all exports they have restuned. This way the end result is still not waht we want
188 // but no information is lost.
189 // WE SHOULD fix this behavior, but this is ONLY possible if we can treat many exports as no exports for the sake of singles
190 if (anyExports)
192 allExports = allExports.Concat(exports);
197 return allExports;
201 private void OnExportChangedInternal(object sender, ExportsChangeEventArgs e)
203 this.OnExportsChanged(e);
206 private void OnExportChangingInternal(object sender, ExportsChangeEventArgs e)
208 this.OnExportsChanging(e);
211 private void ThrowIfDisposed()
213 if (this._isDisposed == 1)
215 throw ExceptionBuilder.CreateObjectDisposed(this);