/// <summary> /// Sets the imports of the specified composable part exactly once and they will not /// ever be recomposed. /// </summary> /// <param name="part"> /// The <see cref="ComposablePart"/> to set the imports. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="part"/> is <see langword="null"/>. /// </exception> /// <exception cref="CompositionException"> /// An error occurred during composition. <see cref="CompositionException.Errors"/> will /// contain a collection of errors that occurred. /// </exception> /// <exception cref="ObjectDisposedException"> /// The <see cref="ICompositionService"/> has been disposed of. /// </exception> public void SatisfyImportsOnce(ComposablePart part) { this.ThrowIfDisposed(); if (this._importEngine == null) { ImportEngine importEngine = new ImportEngine(this, this._isThreadSafe); lock (this._lock) { if (this._importEngine == null) { Thread.MemoryBarrier(); this._importEngine = importEngine; importEngine = null; } } if (importEngine != null) { importEngine.Dispose(); } } this._importEngine.SatisfyImportsOnce(part); }
private object GetExportedValue(CatalogPart part, ExportDefinition export, bool isSharedPart) { ThrowIfDisposed(); EnsureRunning(); Assumes.NotNull(part, export); // We don't protect against thread racing here, as "importsSatisfied" is merely an optimization // if two threads satisfy imports twice, the results is the same, just the perf hit is heavier. bool importsSatisfied = part.ImportsSatisfied; ImportEngine importEngine = importsSatisfied ? null : _importEngine; object exportedValue = CompositionServices.GetExportedValueFromComposedPart( importEngine, part.Part, export); if (!importsSatisfied) { // and set "ImportsSatisfied" to true part.ImportsSatisfied = true; } // Only hold conditional references for recomposable non-shared parts because we are // already holding strong references to the shared parts. if (exportedValue != null && !isSharedPart && part.Part.IsRecomposable()) { PreventPartCollection(exportedValue, part.Part); } return(exportedValue); }
public void PreviewImports_Unsuccessful_NoAtomicComposition_ShouldNotBlockChanges() { var exportProvider = ExportProviderFactory.CreateRecomposable(); var engine = new ImportEngine(exportProvider); var import = ImportDefinitionFactory.Create("Value"); var importer = PartFactory.CreateImporter(import); ExceptionAssert.Throws<CompositionException>(() => engine.PreviewImports(importer, null)); exportProvider.AddExport("Value", 22); exportProvider.AddExport("Value", 23); exportProvider.RemoveExport("Value"); GC.KeepAlive(importer); }
public CompositionContainer(ComposablePartCatalog catalog, bool isThreadSafe, params ExportProvider[] providers) { this._importEngine = new ImportEngine(this, isThreadSafe); this._partExportProvider = new ComposablePartExportProvider(isThreadSafe); this._partExportProvider.SourceProvider = this; this._providers = new ReadOnlyCollection <ExportProvider>(providers != null ? (ExportProvider[])providers.Clone() : new ExportProvider[0]); List <ExportProvider> providerList = new List <ExportProvider>(); providerList.Add(this._partExportProvider); if (catalog != null) { this._catalogExportProvider = new CatalogExportProvider(catalog, isThreadSafe); this._catalogExportProvider.SourceProvider = this; providerList.Add(this._catalogExportProvider); } foreach (var provider in this._providers) { if (provider == null) { throw ExceptionBuilder.CreateContainsNullElement("providers"); } providerList.Add(provider); } // we only build the aggregating provider if necessary - that is, if we have more than one provider to aggregate if (providerList.Count > 1) { this._aggregatingExportProvider = new AggregateExportProvider(providerList); this._rootProvider = this._aggregatingExportProvider; } else { Assumes.IsTrue(providerList.Count == 1); this._rootProvider = providerList[0]; } this._rootProvider.ExportsChanged += this.OnExportsChangedInternal; this._rootProvider.ExportsChanging += this.OnExportsChangingInternal; }
internal static object GetExportedValueFromComposedPart(ImportEngine engine, ComposablePart part, ExportDefinition definition) { try { engine.SatisfyImports(part); } catch (CompositionException ex) { throw ExceptionBuilder.CreateCannotGetExportedValue(part, definition, ex); } try { return(part.GetExportedValue(definition)); } catch (ComposablePartException ex) { throw ExceptionBuilder.CreateCannotGetExportedValue(part, definition, ex); } }
/// <summary> /// Sets the imports of the specified composable part exactly once and they will not /// ever be recomposed. /// </summary> /// <param name="part"> /// The <see cref="ComposablePart"/> to set the imports. /// </param> /// <exception cref="ArgumentNullException"> /// <paramref name="part"/> is <see langword="null"/>. /// </exception> /// <exception cref="CompositionException"> /// An error occurred during composition. <see cref="CompositionException.Errors"/> will /// contain a collection of errors that occurred. /// </exception> /// <exception cref="ObjectDisposedException"> /// The <see cref="ICompositionService"/> has been disposed of. /// </exception> public void SatisfyImportsOnce(ComposablePart part) { ThrowIfDisposed(); if (_importEngine == null) { ImportEngine?importEngine = new ImportEngine(this, _compositionOptions); lock (_lock) { if (_importEngine == null) { Thread.MemoryBarrier(); _importEngine = importEngine; importEngine = null; } } importEngine?.Dispose(); } _importEngine.SatisfyImportsOnce(part); }
/// <summary> /// Releases unmanaged and - optionally - managed resources /// </summary> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> protected virtual void Dispose(bool disposing) { if (disposing) { if (!_isDisposed) { bool disposeLock = false; ImportEngine importEngine = null; try { using (_lock.LockStateForWrite()) { if (!_isDisposed) { importEngine = _importEngine; _importEngine = null; _sourceProvider = null; _isDisposed = true; disposeLock = true; } } } finally { if (importEngine != null) { importEngine.Dispose(); } if (disposeLock) { _lock.Dispose(); } } } } }
/// <summary> /// Releases unmanaged and - optionally - managed resources /// </summary> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> protected virtual void Dispose(bool disposing) { if (disposing) { if (!this._isDisposed) { bool disposeLock = false; ImportEngine oldImportEngine = null; try { using (this._lock.LockStateForWrite()) { if (!this._isDisposed) { oldImportEngine = this._importEngine; this._importEngine = null; this._sourceProvider = null; this._isDisposed = true; disposeLock = true; } } } finally { if (oldImportEngine != null) { oldImportEngine.Dispose(); } if (disposeLock) { this._lock.Dispose(); } } } } }
public void PreviewImports_Successful_AtomicComposition_Completeted_ShouldBlockChanges() { var exportProvider = ExportProviderFactory.CreateRecomposable(); var engine = new ImportEngine(exportProvider); var import = ImportDefinitionFactory.Create("Value"); var importer = PartFactory.CreateImporter(import); exportProvider.AddExport("Value", 21); using (var atomicComposition = new AtomicComposition()) { engine.PreviewImports(importer, atomicComposition); atomicComposition.Complete(); } ExceptionAssert.Throws<ChangeRejectedException>(() => exportProvider.AddExport("Value", 22)); ExceptionAssert.Throws<ChangeRejectedException>(() => exportProvider.RemoveExport("Value")); GC.KeepAlive(importer); }
/// <summary> /// Releases unmanaged and - optionally - managed resources /// </summary> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> protected virtual void Dispose(bool disposing) { if (disposing) { if (!this._isDisposed) { bool disposeLock = false; INotifyComposablePartCatalogChanged catalogToUnsubscribeFrom = null; HashSet <IDisposable> partsToDispose = null; ImportEngine importEngine = null; ExportProvider sourceProvider = null; try { using (this._lock.LockStateForWrite()) { if (!this._isDisposed) { catalogToUnsubscribeFrom = this._catalog as INotifyComposablePartCatalogChanged; this._catalog = null; sourceProvider = this._sourceProvider; this._sourceProvider = null; importEngine = this._importEngine; this._importEngine = null; partsToDispose = this._partsToDispose; this._gcRoots = null; disposeLock = true; this._isDisposed = true; } } } finally { if (catalogToUnsubscribeFrom != null) { catalogToUnsubscribeFrom.Changing -= this.OnCatalogChanging; } if (sourceProvider != null) { sourceProvider.ExportsChanging -= this.OnExportsChangingInternal; } if (importEngine != null) { importEngine.Dispose(); } if (partsToDispose != null) { foreach (var part in partsToDispose) { part.Dispose(); } } if (disposeLock) { this._lock.Dispose(); } } } } }
public void SatisfyImports_Recomposable_Unregister_ValueShouldChangeOnce() { var exportProvider = ExportProviderFactory.CreateRecomposable(); var engine = new ImportEngine(exportProvider); exportProvider.AddExport("Value", 21); var import = ImportDefinitionFactory.Create("Value", true); var importer = PartFactory.CreateImporter(import); engine.SatisfyImports(importer); Assert.AreEqual(21, importer.GetImport(import)); exportProvider.ReplaceExportValue("Value", 42); Assert.AreEqual(42, importer.GetImport(import), "Value should change!"); engine.ReleaseImports(importer, null); exportProvider.ReplaceExportValue("Value", 666); Assert.AreEqual(42, importer.GetImport(import), "Value should not change!"); GC.KeepAlive(importer); }
public PartManager(ImportEngine importEngine, ComposablePart part) { _importEngine = importEngine; _part = part; }
public EngineContext(ImportEngine importEngine, EngineContext parentEngineContext) { this._importEngine = importEngine; this._parentEngineContext = parentEngineContext; }
public EngineContext(ImportEngine importEngine, EngineContext parentEngineContext) { _importEngine = importEngine; _parentEngineContext = parentEngineContext; }
public void PreviewImports_Successful_AtomicComposition_RolledBack_ShouldNotBlockChanges() { var exportProvider = ExportProviderFactory.CreateRecomposable(); var engine = new ImportEngine(exportProvider); var import = ImportDefinitionFactory.Create("Value"); var importer = PartFactory.CreateImporter(import); exportProvider.AddExport("Value", 21); using (var atomicComposition = new AtomicComposition()) { engine.PreviewImports(importer, atomicComposition); // Let atomicComposition get disposed thus rolledback } exportProvider.AddExport("Value", 22); exportProvider.RemoveExport("Value"); GC.KeepAlive(importer); }
public void SatisfyImportsOnce_Successful_ShouldNotBlockChanges() { var exportProvider = ExportProviderFactory.CreateRecomposable(); var engine = new ImportEngine(exportProvider); var import = ImportDefinitionFactory.Create("Value"); var importer = PartFactory.CreateImporter(import); exportProvider.AddExport("Value", 21); engine.SatisfyImportsOnce(importer); exportProvider.AddExport("Value", 22); exportProvider.RemoveExport("Value"); GC.KeepAlive(importer); }
public void SatisfyImports_ZeroCollectionImport_Recomposable_ShouldNotBlockChanges() { var exportProvider = ExportProviderFactory.CreateRecomposable(); var engine = new ImportEngine(exportProvider); var import = ImportDefinitionFactory.Create("Value", ImportCardinality.ZeroOrMore, true, false); var importer = PartFactory.CreateImporter(import); exportProvider.AddExport("Value", 20); engine.SatisfyImports(importer); exportProvider.AddExport("Value", 21); exportProvider.RemoveExport("Value"); GC.KeepAlive(importer); }
public PartManager(ImportEngine importEngine, ComposablePart part) { this._importEngine = importEngine; this._part = part; }
public void SatisfyImports_MissingOptionalImport_NonRecomposable_ShouldNotBlockChanges() { var exportProvider = ExportProviderFactory.CreateRecomposable(); var engine = new ImportEngine(exportProvider); var import = ImportDefinitionFactory.Create("Value", ImportCardinality.ZeroOrOne, false, false); var importer = PartFactory.CreateImporter(import); exportProvider.AddExport("Value", 20); engine.SatisfyImports(importer); ExceptionAssert.Throws<ChangeRejectedException>(() => exportProvider.AddExport("Value", 21)); ExceptionAssert.Throws<ChangeRejectedException>(() => exportProvider.RemoveExport("Value")); GC.KeepAlive(importer); }
public void PreviewImports_ReleaseImports_ShouldNotBlockChanges() { var exportProvider = ExportProviderFactory.CreateRecomposable(); var engine = new ImportEngine(exportProvider); var import = ImportDefinitionFactory.Create("Value"); var importer = PartFactory.CreateImporter(import); exportProvider.AddExport("Value", 21); engine.PreviewImports(importer, null); ExceptionAssert.Throws<ChangeRejectedException>(() => exportProvider.AddExport("Value", 22)); ExceptionAssert.Throws<ChangeRejectedException>(() => exportProvider.RemoveExport("Value")); engine.ReleaseImports(importer, null); exportProvider.AddExport("Value", 22); exportProvider.RemoveExport("Value"); GC.KeepAlive(importer); }
public void SatisifyImportsOnce_Recomposable_ValueShouldNotChange_NoRecompositionRequested_ViaNonArgumentSignature() { var exportProvider = ExportProviderFactory.CreateRecomposable(); var engine = new ImportEngine(exportProvider); exportProvider.AddExport("Value", 21); var import = ImportDefinitionFactory.Create("Value", true); var importer = PartFactory.CreateImporter(import); engine.SatisfyImportsOnce(importer); Assert.AreEqual(21, importer.GetImport(import)); exportProvider.ReplaceExportValue("Value", 42); Assert.AreEqual(21, importer.GetImport(import), "Value should not change!"); GC.KeepAlive(importer); }
public void PreviewImports_ZeroCollectionImport_ShouldSucceed() { var exportProvider = ExportProviderFactory.CreateRecomposable(); var engine = new ImportEngine(exportProvider); var import = ImportDefinitionFactory.Create("Value", ImportCardinality.ZeroOrMore); var importer = PartFactory.CreateImporter(import); engine.PreviewImports(importer, null); GC.KeepAlive(importer); }
public void PreviewImports_MissingOptionalImport_NonRecomposable_ShouldNotBlockChanges() { var exportProvider = ExportProviderFactory.CreateRecomposable(); var engine = new ImportEngine(exportProvider); var import = ImportDefinitionFactory.Create("Value", ImportCardinality.ZeroOrOne, false, false); var importer = PartFactory.CreateImporter(import); engine.PreviewImports(importer, null); exportProvider.AddExport("Value", 21); exportProvider.AddExport("Value", 22); exportProvider.RemoveExport("Value"); GC.KeepAlive(importer); }
public void SatisfyImports_NonRecomposable_ValueShouldNotChange() { var exportProvider = ExportProviderFactory.CreateRecomposable(); var engine = new ImportEngine(exportProvider); exportProvider.AddExport("Value", 21); var import = ImportDefinitionFactory.Create("Value", false); var importer = PartFactory.CreateImporter(import); engine.SatisfyImports(importer); Assert.AreEqual(21, importer.GetImport(import)); // After rejection batch failures throw ChangeRejectedException to indicate that // the failure did not affect the container ExceptionAssert.Throws<ChangeRejectedException>(() => exportProvider.ReplaceExportValue("Value", 42)); Assert.AreEqual(21, importer.GetImport(import), "Value should not change!"); GC.KeepAlive(importer); }
/// <summary> /// Releases unmanaged and - optionally - managed resources /// </summary> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> protected virtual void Dispose(bool disposing) { if (disposing) { if (!this._isDisposed) { ExportProvider rootProvider = null; AggregateExportProvider aggregatingExportProvider = null; ComposablePartExportProvider partExportProvider = null; CatalogExportProvider catalogExportProvider = null; ImportEngine importEngine = null; lock (this._lock) { if (!this._isDisposed) { rootProvider = this._rootProvider; this._rootProvider = null; aggregatingExportProvider = this._aggregatingExportProvider; this._aggregatingExportProvider = null; partExportProvider = this._partExportProvider; this._partExportProvider = null; catalogExportProvider = this._catalogExportProvider; this._catalogExportProvider = null; importEngine = this._importEngine; this._importEngine = null; this._isDisposed = true; } } if (rootProvider != null) { rootProvider.ExportsChanged -= this.OnExportsChangedInternal; rootProvider.ExportsChanging -= this.OnExportsChangingInternal; } if (aggregatingExportProvider != null) { aggregatingExportProvider.Dispose(); } if (catalogExportProvider != null) { catalogExportProvider.Dispose(); } if (partExportProvider != null) { partExportProvider.Dispose(); } if (importEngine != null) { importEngine.Dispose(); } } } }
public void SatisfyImports_NonRecomposable_Prerequisite_ValueShouldNotChange() { var exportProvider = ExportProviderFactory.CreateRecomposable(); var engine = new ImportEngine(exportProvider); var import = ImportDefinitionFactory.Create("Value", false, true); var importer = PartFactory.CreateImporter(import); exportProvider.AddExport("Value", 21); engine.SatisfyImports(importer); Assert.AreEqual(21, importer.GetImport(import)); ExceptionAssert.Throws<ChangeRejectedException>(() => exportProvider.ReplaceExportValue("Value", 42)); Assert.AreEqual(21, importer.GetImport(import), "Value should NOT change!"); GC.KeepAlive(importer); }
/// <summary> /// Releases unmanaged and - optionally - managed resources /// </summary> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> protected virtual void Dispose(bool disposing) { if (disposing) { if (!_isDisposed) { ExportProvider rootProvider = null; IDisposable disposableAncestorExportProvider = null; IDisposable disposableLocalExportProvider = null; IDisposable disposableRootProvider = null; ComposablePartExportProvider partExportProvider = null; CatalogExportProvider catalogExportProvider = null; ImportEngine importEngine = null; lock (_lock) { if (!_isDisposed) { rootProvider = _rootProvider; _rootProvider = null; disposableRootProvider = _disposableRootProvider; _disposableRootProvider = null; disposableLocalExportProvider = _disposableLocalExportProvider; _disposableLocalExportProvider = null; _localExportProvider = null; disposableAncestorExportProvider = _disposableAncestorExportProvider; _disposableAncestorExportProvider = null; _ancestorExportProvider = null; partExportProvider = _partExportProvider; _partExportProvider = null; catalogExportProvider = _catalogExportProvider; _catalogExportProvider = null; importEngine = _importEngine; _importEngine = null; _isDisposed = true; } } if (rootProvider != null) { rootProvider.ExportsChanged -= OnExportsChangedInternal; rootProvider.ExportsChanging -= OnExportsChangingInternal; } if (disposableRootProvider != null) { disposableRootProvider.Dispose(); } if (disposableAncestorExportProvider != null) { disposableAncestorExportProvider.Dispose(); } if (disposableLocalExportProvider != null) { disposableLocalExportProvider.Dispose(); } if (catalogExportProvider != null) { catalogExportProvider.Dispose(); } if (partExportProvider != null) { partExportProvider.Dispose(); } if (importEngine != null) { importEngine.Dispose(); } } } }
public void SatisfyImports_OneRecomposable_OneNotRecomposable() { var exportProvider = ExportProviderFactory.CreateRecomposable(); var engine = new ImportEngine(exportProvider); var import1 = ImportDefinitionFactory.Create("Value", true); var import2 = ImportDefinitionFactory.Create("Value", false); var importer = PartFactory.CreateImporter(import1, import2); exportProvider.AddExport("Value", 21); engine.SatisfyImports(importer); // Initial compose values should be 21 Assert.AreEqual(21, importer.GetImport(import1)); Assert.AreEqual(21, importer.GetImport(import2)); // Reset value to ensure it doesn't get set to same value again importer.ResetImport(import1); importer.ResetImport(import2); ExceptionAssert.Throws<ChangeRejectedException>(() => exportProvider.ReplaceExportValue("Value", 42)); Assert.AreEqual(null, importer.GetImport(import1), "Value should NOT have been set!"); Assert.AreEqual(null, importer.GetImport(import2), "Value should NOT have been set!"); GC.KeepAlive(importer); }
/// <summary> /// Releases unmanaged and - optionally - managed resources /// </summary> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> protected virtual void Dispose(bool disposing) { if (disposing) { if (!_isDisposed) { //Note: We do not dispose _lock on dispose because DisposePart needs it to check isDisposed state // to eliminate race conditions between it and Dispose INotifyComposablePartCatalogChanged catalogToUnsubscribeFrom = null; HashSet <IDisposable> partsToDispose = null; ImportEngine importEngine = null; ExportProvider sourceProvider = null; AggregateExportProvider aggregateExportProvider = null; try { using (_lock.LockStateForWrite()) { if (!_isDisposed) { catalogToUnsubscribeFrom = _catalog as INotifyComposablePartCatalogChanged; _catalog = null; aggregateExportProvider = _innerExportProvider as AggregateExportProvider; _innerExportProvider = null; sourceProvider = _sourceProvider; _sourceProvider = null; importEngine = _importEngine; _importEngine = null; partsToDispose = _partsToDispose; _gcRoots = null; _isDisposed = true; } } } finally { if (catalogToUnsubscribeFrom != null) { catalogToUnsubscribeFrom.Changing -= OnCatalogChanging; } if (aggregateExportProvider != null) { aggregateExportProvider.Dispose(); } if (sourceProvider != null) { sourceProvider.ExportsChanging -= OnExportsChangingInternal; } if (importEngine != null) { importEngine.Dispose(); } if (partsToDispose != null) { foreach (var part in partsToDispose) { part.Dispose(); } } } } } }
public void SatisfyImports_TwoRecomposables_SingleExportValueChanged() { var exportProvider = ExportProviderFactory.CreateRecomposable(); var engine = new ImportEngine(exportProvider); var import1 = ImportDefinitionFactory.Create("Value1", true); var import2 = ImportDefinitionFactory.Create("Value2", true); var importer = PartFactory.CreateImporter(import1, import2); exportProvider.AddExport("Value1", 21); exportProvider.AddExport("Value2", 23); engine.SatisfyImports(importer); Assert.AreEqual(21, importer.GetImport(import1)); Assert.AreEqual(23, importer.GetImport(import2)); importer.ResetImport(import1); importer.ResetImport(import2); // Only change Value1 exportProvider.ReplaceExportValue("Value1", 42); Assert.AreEqual(42, importer.GetImport(import1), "Value should have been set!"); Assert.AreEqual(null, importer.GetImport(import2), "Value should NOT have changed to the value in the container."); GC.KeepAlive(importer); }
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(); } }); } }