private void ReleasePart(object exportedValue, CatalogPart catalogPart, AtomicComposition atomicComposition) { this.ThrowIfDisposed(); this.EnsureRunning(); Assumes.NotNull(catalogPart); this._importEngine.ReleaseImports(catalogPart.Part, atomicComposition); if (exportedValue != null) { atomicComposition.AddCompleteActionAllowNull(() => { this.AllowPartCollection(exportedValue); }); } IDisposable diposablePart = catalogPart.Part as IDisposable; if (diposablePart != null) { atomicComposition.AddCompleteActionAllowNull(() => { bool removed = false; using (this._lock.LockStateForWrite()) { removed = this._partsToDispose.Remove(diposablePart); } if (removed) { diposablePart.Dispose(); } }); } }
private void OnCatalogChanging(object sender, ComposablePartCatalogChangeEventArgs e) { using (var atomicComposition = new AtomicComposition(e.AtomicComposition)) { // Save the preview catalog to use in place of the original while handling // this event atomicComposition.SetValue(this._catalog, new CatalogChangeProxy(this._catalog, e.AddedDefinitions, e.RemovedDefinitions)); IEnumerable <ExportDefinition> addedExports = e.AddedDefinitions .SelectMany(part => part.ExportDefinitions) .ToArray(); IEnumerable <ExportDefinition> removedExports = e.RemovedDefinitions .SelectMany(part => part.ExportDefinitions) .ToArray(); // Remove any parts based on eliminated definitions (in a atomicComposition-friendly // fashion) foreach (var definition in e.RemovedDefinitions) { ComposablePart removedPart = null; bool removed = false; using (new ReadLock(_lock)) { removed = this._activatedParts.TryGetValue(definition, out removedPart); } if (removed) { ReleasePart(null, removedPart, atomicComposition); atomicComposition.AddCompleteActionAllowNull(() => { using (new WriteLock(_lock)) { this._activatedParts.Remove(definition); } }); } } UpdateRejections(addedExports.ConcatAllowingNull(removedExports), atomicComposition); this.OnExportsChanging( new ExportsChangeEventArgs(addedExports, removedExports, atomicComposition)); atomicComposition.AddCompleteAction(() => this.OnExportsChanged( new ExportsChangeEventArgs(addedExports, removedExports, null))); atomicComposition.Complete(); } }
private void ReleasePart(object exportedValue, ComposablePart part, AtomicComposition atomicComposition) { this.ThrowIfDisposed(); this.EnsureRunning(); Assumes.NotNull(part); this._importEngine.ReleaseImports(part, atomicComposition); if (exportedValue != null) { atomicComposition.AddCompleteActionAllowNull(() => { using (new WriteLock(this._lock)) { this._conditionalReferencesForRecomposableParts.Remove(exportedValue); } }); } IDisposable diposablePart = part as IDisposable; if (diposablePart != null) { atomicComposition.AddCompleteActionAllowNull(() => { bool removed = false; using (new WriteLock(this._lock)) { removed = this._partsToDispose.Remove(diposablePart); } if (removed) { diposablePart.Dispose(); } }); } }
private void OnCatalogChanging(object sender, ComposablePartCatalogChangeEventArgs e) { using (var atomicComposition = new AtomicComposition(e.AtomicComposition)) { // Save the preview catalog to use in place of the original while handling // this event atomicComposition.SetValue(_catalog, new CatalogChangeProxy(_catalog, e.AddedDefinitions, e.RemovedDefinitions)); IEnumerable <ExportDefinition> addedExports = GetExportsFromPartDefinitions(e.AddedDefinitions); IEnumerable <ExportDefinition> removedExports = GetExportsFromPartDefinitions(e.RemovedDefinitions); // Remove any parts based on eliminated definitions (in a atomicComposition-friendly // fashion) foreach (var definition in e.RemovedDefinitions) { CatalogPart removedPart = null; bool removed = false; using (_lock.LockStateForRead()) { removed = _activatedParts.TryGetValue(definition, out removedPart); } if (removed) { var capturedDefinition = definition; DisposePart(null, removedPart, atomicComposition); atomicComposition.AddCompleteActionAllowNull(() => { using (_lock.LockStateForWrite()) { _activatedParts.Remove(capturedDefinition); } }); } } UpdateRejections(addedExports.ConcatAllowingNull(removedExports), atomicComposition); OnExportsChanging( new ExportsChangeEventArgs(addedExports, removedExports, atomicComposition)); atomicComposition.AddCompleteAction(() => OnExportsChanged( new ExportsChangeEventArgs(addedExports, removedExports, null))); atomicComposition.Complete(); } }
private bool DetermineRejection(ComposablePartDefinition definition, AtomicComposition parentAtomicComposition) { ChangeRejectedException exception = null; // if there is no active atomic composition and rejection is disabled, there's no need to do any of the below if (!EnsureRejection(parentAtomicComposition)) { return(false); } using (var localAtomicComposition = new AtomicComposition(parentAtomicComposition)) { // The part definition we're currently working on is treated optimistically // as if we know it hasn't been rejected. This handles recursion, and if we // later decide that it has been rejected we'll discard all nested progress so // all side-effects of the mistake are erased. // // Note that this means that recursive failures that would be detected by the // import engine are not discovered by rejection currently. Loops among // prerequisites, runaway import chains involving factories, and prerequisites // that cannot be fully satisfied still result in runtime errors. Doing // otherwise would be possible but potentially expensive - and could be a v2 // improvement if deemed worthwhile. UpdateAtomicCompositionQueryForPartEquals(localAtomicComposition, definition, AtomicCompositionQueryState.TreatAsValidated); var newPart = definition.CreatePart(); try { _importEngine.PreviewImports(newPart, localAtomicComposition); // Reuse the partially-fleshed out part the next time we need a shared // instance to keep the expense of pre-validation to a minimum. Note that // _activatedParts holds references to both shared and non-shared parts. // The non-shared parts will only be used for rejection purposes only but // the shared parts will be handed out when requested via GetExports as // well as be used for rejection purposes. localAtomicComposition.AddCompleteActionAllowNull(() => { using (_lock.LockStateForWrite()) { if (!_activatedParts.ContainsKey(definition)) { _activatedParts.Add(definition, new CatalogPart(newPart)); IDisposable newDisposablePart = newPart as IDisposable; if (newDisposablePart != null) { _partsToDispose.Add(newDisposablePart); } } } }); // Success! Complete any recursive work that was conditioned on this part's validation localAtomicComposition.Complete(); return(false); } catch (ChangeRejectedException ex) { exception = ex; } } // If we've reached this point then this part has been rejected so we need to // record the rejection in our parent composition or execute it immediately if // one doesn't exist. parentAtomicComposition.AddCompleteActionAllowNull(() => { using (_lock.LockStateForWrite()) { _rejectedParts.Add(definition); } CompositionTrace.PartDefinitionRejected(definition, exception); }); if (parentAtomicComposition != null) { UpdateAtomicCompositionQueryForPartEquals(parentAtomicComposition, definition, AtomicCompositionQueryState.TreatAsRejected); } return(true); }
private void DisposePart(object exportedValue, CatalogPart catalogPart, AtomicComposition atomicComposition) { Assumes.NotNull(catalogPart); if (_isDisposed) { return; } ImportEngine importEngine = null; using (_lock.LockStateForWrite()) { if (_isDisposed) { return; } importEngine = _importEngine; } if (importEngine != null) { importEngine.ReleaseImports(catalogPart.Part, atomicComposition); } if (exportedValue != null) { atomicComposition.AddCompleteActionAllowNull(() => { AllowPartCollection(exportedValue); }); } IDisposable diposablePart = catalogPart.Part as IDisposable; if (diposablePart != null) { atomicComposition.AddCompleteActionAllowNull(() => { bool removed = false; if (_isDisposed) { return; } using (_lock.LockStateForWrite()) { if (_isDisposed) { return; } removed = _partsToDispose.Remove(diposablePart); } if (removed) { diposablePart.Dispose(); } }); } }
private bool DetermineRejection(ComposablePartDefinition definition, AtomicComposition parentAtomicComposition) { using (var localAtomicComposition = new AtomicComposition(parentAtomicComposition)) { // The part definition we're currently working on is treated optimistically // as if we know it hasn't been rejected. This handles recursion, and if we // later decide that it has been rejected we'll discard all nested progress so // all side-effects of the mistake are erased. // // Note that this means that recursive failures that would be detected by the // import engine are not discovered by rejection currently. Loops among // prerequisites, runaway import chains involving factories, and prerequisites // that cannot be fully satisfied still result in runtime errors. Doing // otherwise would be possible but potentially expensive - and could be a v2 // improvement if deemed worthwhile. UpdateAtomicCompositionQuery(localAtomicComposition, def => definition.Equals(def), AtomicCompositionQueryState.TreatAsValidated); var newPart = definition.CreatePart(); try { this._importEngine.PreviewImports(newPart, localAtomicComposition); // Reuse the partially-fleshed out part the next time we need a shared // instance to keep the expense of pre-validation to a minimum. Note that // _activatedParts holds references to both shared and non-shared parts. // The non-shared parts will only be used for rejection purposes only but // the shared parts will be handed out when requested via GetExports as // well as be used for rejection purposes. localAtomicComposition.AddCompleteActionAllowNull(() => { using (new WriteLock(this._lock)) { if (!this._activatedParts.ContainsKey(definition)) { this._activatedParts.Add(definition, newPart); IDisposable newDisposablePart = newPart as IDisposable; if (newDisposablePart != null) { this._partsToDispose.Add(newDisposablePart); } } } }); // Success! Complete any recursive work that was conditioned on this part's validation localAtomicComposition.Complete(); return(false); } catch (ChangeRejectedException exception) { // We should perhaps add non-debug tracing of these errors to help diagosis // composition issues related to parts being rejected. Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Part {0} was rejected because of the following issue:", newPart)); Debug.WriteLine(exception.Message); } } // If we've reached this point then this part has been rejected so we need to // record the rejection in our parent atomicComposition parentAtomicComposition.AddCompleteActionAllowNull(() => { using (new WriteLock(this._lock)) { this._rejectedParts.Add(definition); } }); if (parentAtomicComposition != null) { UpdateAtomicCompositionQuery(parentAtomicComposition, def => definition.Equals(def), AtomicCompositionQueryState.TreatAsRejected); } return(true); }