1
// -----------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 // -----------------------------------------------------------------------
5 using System
.Collections
.Generic
;
6 using System
.Collections
.ObjectModel
;
7 using System
.ComponentModel
.Composition
.Primitives
;
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;
21 /// Initializes a new instance of the <see cref="AggregateExportProvider"/> class.
23 /// <param name="providers">The prioritized list of export providers.</param>
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)"/>.
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.
34 public AggregateExportProvider(params ExportProvider
[] providers
)
35 : this(providers
.AsEnumerable())
40 /// Initializes a new instance of the <see cref="AggregateExportProvider"/> class.
42 /// <param name="providers">The prioritized list of export providers. The providers are consulted in order in which they are supplied.</param>
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)"/>.
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.
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
)
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
);
79 /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
84 GC
.SuppressFinalize(this);
88 /// Releases unmanaged and - optionally - managed resources
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
)
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
;
110 /// Gets the export providers which the aggregate export provider aggregates.
113 /// A <see cref="ReadOnlyCollection{T}"/> of <see cref="ExportProvider"/> objects
114 /// which the <see cref="AggregateExportProvider"/> aggregates.
116 /// <exception cref="ObjectDisposedException">
117 /// The <see cref="AggregateExportProvider"/> has been disposed of.
119 public ReadOnlyCollection
<ExportProvider
> Providers
123 this.ThrowIfDisposed();
125 return this._providers
;
130 /// Returns all exports that match the conditions of the specified import.
132 /// <param name="definition">The <see cref="ImportDefinition"/> that defines the conditions of the
133 /// <see cref="Export"/> to get.</param>
134 /// <returns></returns>
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}"/>.
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"/>.
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
))
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.
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
192 allExports
= allExports
.Concat(exports
);
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);