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);
        }
예제 #11
0
        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 !);
        }
예제 #12
0
        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();
            }
        }
예제 #14
0
파일: ImportEngine.cs 프로젝트: zqb971/mef
        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);
        }
예제 #15
0
        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));
        }
예제 #17
0
        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);
        }
예제 #21
0
        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();
        }