1 // -----------------------------------------------------------------------
\r
2 // Copyright (c) Microsoft Corporation. All rights reserved.
\r
3 // -----------------------------------------------------------------------
\r
5 using System.Collections.Generic;
\r
6 using System.ComponentModel.Composition.Primitives;
\r
7 using System.Diagnostics;
\r
9 using System.Runtime.CompilerServices;
\r
10 using Microsoft.Internal;
\r
11 using Microsoft.Internal.Collections;
\r
13 namespace System.ComponentModel.Composition.Hosting
\r
15 public partial class ImportEngine
\r
18 /// Used by the <see cref="ImportEngine"/> to manage the composition of a given part.
\r
19 /// It stores things like the list of disposable exports used to satisfy the imports as
\r
20 /// well as the caching of the exports discovered during previewing of a part.
\r
22 private class PartManager
\r
24 private Dictionary<ImportDefinition, List<IDisposable>> _importedDisposableExports;
\r
25 private Dictionary<ImportDefinition, Export[]> _importCache;
\r
26 private string[] _importedContractNames;
\r
27 private ComposablePart _part;
\r
28 private ImportState _state = ImportState.NoImportsSatisfied;
\r
29 private readonly ImportEngine _importEngine;
\r
31 public PartManager(ImportEngine importEngine, ComposablePart part)
\r
33 this._importEngine = importEngine;
\r
37 public ComposablePart Part
\r
45 public ImportState State
\r
49 using (this._importEngine._lock.LockStateForRead())
\r
56 using (this._importEngine._lock.LockStateForWrite())
\r
58 this._state = value;
\r
63 public bool TrackingImports { get; set; }
\r
65 public IEnumerable<string> GetImportedContractNames()
\r
67 if (this.Part == null)
\r
69 return Enumerable.Empty<string>();
\r
72 if (this._importedContractNames == null)
\r
74 this._importedContractNames = this.Part.ImportDefinitions.Select(import => import.ContractName ?? ImportDefinition.EmptyContractName).Distinct().ToArray();
\r
76 return this._importedContractNames;
\r
79 public CompositionResult TrySetImport(ImportDefinition import, IEnumerable<Export> exports)
\r
83 this.Part.SetImport(import, exports);
\r
84 UpdateDisposableDependencies(import, exports);
\r
85 return CompositionResult.SucceededResult;
\r
87 catch (CompositionException ex)
\r
88 { // Pulling on one of the exports failed
\r
90 return new CompositionResult(
\r
91 ErrorBuilder.CreatePartCannotSetImport(Part, import, ex));
\r
93 catch (ComposablePartException ex)
\r
94 { // Type mismatch between export and import
\r
96 return new CompositionResult(
\r
97 ErrorBuilder.CreatePartCannotSetImport(Part, import, ex));
\r
101 public void SetSavedImport(ImportDefinition import, Export[] exports, AtomicComposition atomicComposition)
\r
103 if (atomicComposition != null)
\r
105 var savedExports = this.GetSavedImport(import);
\r
107 // Add a revert action to revert the stored exports
\r
108 // in the case that this atomicComposition gets rolled back.
\r
109 atomicComposition.AddRevertAction(() =>
\r
110 this.SetSavedImport(import, savedExports, null));
\r
113 if (this._importCache == null)
\r
115 this._importCache = new Dictionary<ImportDefinition, Export[]>();
\r
118 this._importCache[import] = exports;
\r
121 public Export[] GetSavedImport(ImportDefinition import)
\r
123 Export[] exports = null;
\r
124 if (this._importCache != null)
\r
126 // We don't care about the return value we just want the exports
\r
127 // and if it isn't present we just return the initialized null value
\r
128 this._importCache.TryGetValue(import, out exports);
\r
133 public void ClearSavedImports()
\r
135 this._importCache = null;
\r
138 public CompositionResult TryOnComposed()
\r
142 this.Part.Activate();
\r
143 return CompositionResult.SucceededResult;
\r
145 catch (ComposablePartException ex)
\r
146 { // Type failed to be constructed, imports could not be set, etc
\r
147 return new CompositionResult(
\r
148 ErrorBuilder.CreatePartCannotActivate(this.Part, ex));
\r
152 public void UpdateDisposableDependencies(ImportDefinition import, IEnumerable<Export> exports)
\r
154 // Determine if there are any new disposable exports, optimizing for the most
\r
155 // likely case, which is that there aren't any
\r
156 List<IDisposable> disposableExports = null;
\r
157 foreach (var disposableExport in exports.OfType<IDisposable>())
\r
159 if (disposableExports == null)
\r
161 disposableExports = new List<IDisposable>();
\r
163 disposableExports.Add(disposableExport);
\r
166 // Dispose any existing references previously set on this import
\r
167 List<IDisposable> oldDisposableExports = null;
\r
168 if (this._importedDisposableExports != null &&
\r
169 this._importedDisposableExports.TryGetValue(import, out oldDisposableExports))
\r
171 oldDisposableExports.ForEach(disposable => disposable.Dispose());
\r
173 // If there aren't any replacements, get rid of the old storage
\r
174 if (disposableExports == null)
\r
176 this._importedDisposableExports.Remove(import);
\r
177 if (!this._importedDisposableExports.FastAny())
\r
179 this._importedDisposableExports = null;
\r
186 // Record the new collection
\r
187 if (disposableExports != null)
\r
189 if (this._importedDisposableExports == null)
\r
191 this._importedDisposableExports = new Dictionary<ImportDefinition, List<IDisposable>>();
\r
193 this._importedDisposableExports[import] = disposableExports;
\r
197 public void DisposeAllDependencies()
\r
199 if (this._importedDisposableExports != null)
\r
201 IEnumerable<IDisposable> dependencies = this._importedDisposableExports.Values
\r
202 .SelectMany(exports => exports);
\r
204 this._importedDisposableExports = null;
\r
206 dependencies.ForEach(disposableExport => disposableExport.Dispose());
\r