public void AtomicComposition_CompleteValues() { object key1 = new Object(); object key2 = new Object(); using (var contextA = new AtomicComposition()) { TestNoValue(contextA, key1); TestNoValue(contextA, key2); contextA.SetValue(key1, "Hello"); TestValue(contextA, key1, "Hello"); TestNoValue(contextA, key2); // Try overwriting using (var contextB = new AtomicComposition(contextA)) { TestValue(contextB, key1, "Hello"); TestNoValue(contextB, key2); contextB.SetValue(key1, "Greetings"); TestValue(contextB, key1, "Greetings"); TestNoValue(contextB, key2); contextB.Complete(); } TestValue(contextA, key1, "Greetings"); TestNoValue(contextA, key2); // Try overwrite with revert using (var contextC = new AtomicComposition(contextA)) { TestValue(contextC, key1, "Greetings"); TestNoValue(contextC, key2); contextC.SetValue(key1, "Goodbye"); contextC.SetValue(key2, "Goodbye, Again"); TestValue(contextC, key1, "Goodbye"); TestValue(contextC, key2, "Goodbye, Again"); // Don't complete } TestValue(contextA, key1, "Greetings"); TestNoValue(contextA, key2); contextA.Complete(); } }
private void SetQuery(AtomicComposition context, object key, Func <int, Func <int, bool>, bool> query) { Func <int, bool> parentQuery; context.TryGetValue(key, out parentQuery); Func <int, bool> queryFunction = parameter => { return(query(parameter, parentQuery)); }; context.SetValue(key, queryFunction); }
private void UpdateAtomicCompositionQueryForPartEquals( AtomicComposition atomicComposition, ComposablePartDefinition part, AtomicCompositionQueryState state) { PartQueryStateNode previousNode = GetPartQueryStateNode(atomicComposition); atomicComposition.SetValue(this, new PartEqualsQueryStateNode(part, previousNode, state)); }
private void UpdateAtomicCompositionQueryForPartInHashSet( AtomicComposition atomicComposition, HashSet <ComposablePartDefinition> hashset, AtomicCompositionQueryState state) { PartQueryStateNode previousNode = GetPartQueryStateNode(atomicComposition); atomicComposition.SetValue(this, new PartInHashSetQueryStateNode(hashset, previousNode, state)); }
public void SetValue_ToNull_ShouldBeAllowed() { var ct = new AtomicComposition(); ct.SetValue(ct, null); object value = new object(); Assert.True(ct.TryGetValue(ct, out value)); Assert.Null(value); }
public void SetValue_ValueType_ShouldBeAllowed() { var ct = new AtomicComposition(); ct.SetValue(ct, 45); int value; Assert.True(ct.TryGetValue(ct, out value)); Assert.Equal(45, value); }
public void Dispose_AllMethodsShouldThrow() { var ct = new AtomicComposition(); ct.Dispose(); Assert.Throws <ObjectDisposedException>(() => ct.AddCompleteAction(() => ct = null)); Assert.Throws <ObjectDisposedException>(() => ct.Complete()); Assert.Throws <ObjectDisposedException>(() => ct.SetValue(ct, 10)); object value; Assert.Throws <ObjectDisposedException>(() => ct.TryGetValue(ct, out value)); }
public void AfterComplete_AllMethodsShouldThrow() { var ct = new AtomicComposition(); ct.Complete(); Assert.Throws <InvalidOperationException>(() => ct.AddCompleteAction(() => ct = null)); Assert.Throws <InvalidOperationException>(() => ct.Complete()); Assert.Throws <InvalidOperationException>(() => ct.SetValue(ct, 10)); object value; Assert.Throws <InvalidOperationException>(() => ct.TryGetValue(ct, out value)); }
public void SetValue_ChangeOuterValuesWhileHaveInner_ShouldThrow() { var ct = new AtomicComposition(); var ct2 = new AtomicComposition(ct); var key = new object(); Assert.Throws <InvalidOperationException>(() => ct.SetValue(key, 1)); object value; Assert.False(ct2.TryGetValue(key, out value)); Assert.False(ct.TryGetValue(key, out value)); // remove the inner atomicComposition so the outer one becomes unlocked. ct2.Dispose(); ct.SetValue(key, 2); Assert.True(ct.TryGetValue(key, out value)); Assert.Equal(2, value); }
public void SetValue_Reference_ShouldBeAllowed() { var ct = new AtomicComposition(); var sb = new StringBuilder(); ct.SetValue(ct, sb); StringBuilder value; Assert.True(ct.TryGetValue(ct, out value)); Assert.Equal(sb, value); }
private EngineContext GetEngineContext(AtomicComposition atomicComposition) { ArgumentNullException.ThrowIfNull(atomicComposition); if (!atomicComposition.TryGetValue(this, true, out EngineContext? engineContext)) { atomicComposition.TryGetValue(this, false, out EngineContext? parentContext); engineContext = new EngineContext(this, parentContext); atomicComposition.SetValue(this, engineContext); atomicComposition.AddCompleteAction(engineContext.Complete); } return(engineContext !); }
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 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 EngineContext GetEngineContext(AtomicComposition atomicComposition) { Assumes.NotNull(atomicComposition); EngineContext engineContext; if (!atomicComposition.TryGetValue(this, true, out engineContext)) { EngineContext parentContext; atomicComposition.TryGetValue(this, false, out parentContext); engineContext = new EngineContext(this, parentContext); atomicComposition.SetValue(this, engineContext); atomicComposition.AddCompleteAction(engineContext.Complete); } return(engineContext); }
private void UpdateAtomicCompositionQuery( AtomicComposition atomicComposition, Func <ComposablePartDefinition, bool> query, AtomicCompositionQueryState state) { var parentQuery = GetAtomicCompositionQuery(atomicComposition); Func <ComposablePartDefinition, AtomicCompositionQueryState> newQuery = definition => { if (query(definition)) { return(state); } return(parentQuery(definition)); }; atomicComposition.SetValue(this, newQuery); }
public void NoComplete_ShouldNotCopyValuesToInner() { var innerAtomicComposition = new AtomicComposition(); object value; using (var ct = new AtomicComposition(innerAtomicComposition)) { ct.SetValue(this, 21); Assert.False(innerAtomicComposition.TryGetValue(this, out value)); // Do not call complete } // reverify after dispose Assert.False(innerAtomicComposition.TryGetValue(this, out value)); }
private EngineContext GetEngineContext(AtomicComposition atomicComposition) { if (atomicComposition == null) { throw new ArgumentNullException(nameof(atomicComposition)); } EngineContext engineContext; if (!atomicComposition.TryGetValue(this, true, out engineContext)) { EngineContext parentContext; atomicComposition.TryGetValue(this, false, out parentContext); engineContext = new EngineContext(this, parentContext); atomicComposition.SetValue(this, engineContext); atomicComposition.AddCompleteAction(engineContext.Complete); } return(engineContext); }
public void Complete_ShouldCopyValuesToInner() { var innerAtomicComposition = new AtomicComposition(); object value; using (var ct = new AtomicComposition(innerAtomicComposition)) { ct.SetValue(this, 21); Assert.False(innerAtomicComposition.TryGetValue(this, out value)); ct.Complete(); Assert.True(innerAtomicComposition.TryGetValue(this, out value)); Assert.Equal(21, value); } // reverify after dispose Assert.True(innerAtomicComposition.TryGetValue(this, out value)); Assert.Equal(21, value); }
public void SetValue_CauseResize_ShouldWorkFine() { var ct = new AtomicComposition(); var keys = new List <object>(); var values = new List <object>(); for (int i = 0; i < 20; i++) { var key = new object(); keys.Add(key); values.Add(i); ct.SetValue(key, i); } for (int i = 0; i < keys.Count; i++) { object value; Assert.True(ct.TryGetValue(keys[i], out value)); Assert.Equal(i, value); } }
private void UpdateAtomicCompositionQuery( AtomicComposition atomicComposition, Func <ComposablePartDefinition, bool> query, AtomicCompositionQueryState state) { var parentQuery = GetAtomicCompositionQuery(atomicComposition); Func <ComposablePartDefinition, AtomicCompositionQueryState> newQuery = definition => { if (query(definition)) { if (state == AtomicCompositionQueryState.TreatAsValidated) { // Graph is in a cycle atomicComposition.CompositionInCycle = true; } return(state); } return(parentQuery(definition)); }; atomicComposition.SetValue(this, newQuery); }
public void Compose(CompositionBatch batch) { ThrowIfDisposed(); EnsureRunning(); Requires.NotNull(batch, nameof(batch)); // Quick exit test can be done prior to cloning since it's just an optimization, not a // change in behavior if ((batch.PartsToAdd.Count == 0) && (batch.PartsToRemove.Count == 0)) { return; } CompositionResult result = CompositionResult.SucceededResult; // Get updated parts list and a cloned batch var newParts = GetUpdatedPartsList(ref batch); // Allow only recursive calls from the import engine to see the changes until // they've been verified ... using (var atomicComposition = new AtomicComposition()) { // Don't allow reentrant calls to compose during previewing to prevent // corrupted state. if (_currentlyComposing) { throw new InvalidOperationException(SR.ReentrantCompose); } _currentlyComposing = true; try { // In the meantime recursive calls need to be able to see the list as well atomicComposition.SetValue(this, newParts); // Recompose any existing imports effected by the these changes first so that // adapters, resurrected parts, etc. can all play their role in satisfying // imports for added parts Recompose(batch, atomicComposition); // Ensure that required imports can be satisfied foreach (ComposablePart part in batch.PartsToAdd) { // collect the result of previewing all the adds in the batch try { ImportEngine.PreviewImports(part, atomicComposition); } catch (ChangeRejectedException ex) { result = result.MergeResult(new CompositionResult(ex.Errors)); } } result.ThrowOnErrors(atomicComposition); // Complete the new parts since they passed previewing.` using (_lock.LockStateForWrite()) { _parts = newParts; } atomicComposition.Complete(); } finally { _currentlyComposing = false; } } // Satisfy Imports // - Satisfy imports on all newly added component parts foreach (ComposablePart part in batch.PartsToAdd) { result = result.MergeResult(CompositionServices.TryInvoke(() => ImportEngine.SatisfyImports(part))); } // return errors result.ThrowOnErrors(); }
public void Compose(CompositionBatch batch) { this.ThrowIfDisposed(); this.EnsureRunning(); Requires.NotNull(batch, "batch"); // Quick exit test can be done prior to cloning since it's just an optimization, not a // change in behavior if ((batch.PartsToAdd.Count == 0) && (batch.PartsToRemove.Count == 0)) { return; } CompositionResult result = CompositionResult.SucceededResult; // Clone the batch, so that the external changes wouldn't happen half-way thorugh compose // NOTE : this does not guarantee the atomicity of cloning, which is not the goal anyway, // rather the fact that all subsequent calls will deal with an unchanging batch batch = new CompositionBatch(batch.PartsToAdd, batch.PartsToRemove); var newParts = GetUpdatedPartsList(batch); // Allow only recursive calls from the import engine to see the changes until // they've been verified ... using (var atomicComposition = new AtomicComposition()) { // Don't allow reentrant calls to compose during previewing to prevent // corrupted state. if (this._currentlyComposing) { throw new InvalidOperationException(Strings.ReentrantCompose); } this._currentlyComposing = true; try { // In the meantime recursive calls need to be able to see the list as well atomicComposition.SetValue(this, newParts); // Recompose any existing imports effected by the these changes first so that // adapters, resurrected parts, etc. can all play their role in satisfying // imports for added parts this.Recompose(batch, atomicComposition); // Ensure that required imports can be satisfied foreach (ComposablePart part in batch.PartsToAdd) { // collect the result of previewing all the adds in the batch try { this._importEngine.PreviewImports(part, atomicComposition); } catch (ChangeRejectedException ex) { result = result.MergeResult(new CompositionResult(ex.Errors)); } } result.ThrowOnErrors(atomicComposition); // Complete the new parts since they passed previewing.` using (this._lock.LockStateForWrite()) { this._parts = newParts; } atomicComposition.Complete(); } finally { this._currentlyComposing = false; } } // Satisfy Imports // - Satisfy imports on all newly added component parts foreach (ComposablePart part in batch.PartsToAdd) { result = result.MergeResult(CompositionServices.TryInvoke(() => this._importEngine.SatisfyImports(part))); } // return errors result.ThrowOnErrors(); }