Click here to Skip to main content
15,897,718 members
Articles / DevOps / Testing

Composite Application Reloaded

Rate me:
Please Sign up or sign in to vote.
4.88/5 (38 votes)
11 May 2011CPOL12 min read 118.5K   1.5K   95  
A much simpler composite application library.
// -----------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Primitives;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.Internal;
using Microsoft.Internal.Collections;

namespace System.ComponentModel.Composition.Hosting
{
    public partial class ImportEngine
    {
        /// <summary>
        ///     Used by the <see cref="ImportEngine"/> to manage the composition of a given part.
        ///     It stores things like the list of disposable exports used to satisfy the imports as
        ///     well as the caching of the exports discovered during previewing of a part.
        /// </summary>
        private class PartManager
        {
            private Dictionary<ImportDefinition, List<IDisposable>> _importedDisposableExports;
            private Dictionary<ImportDefinition, Export[]> _importCache;
            private string[] _importedContractNames;
            private ComposablePart _part;
            private ImportState _state = ImportState.NoImportsSatisfied;
            private readonly ImportEngine _importEngine;

            public PartManager(ImportEngine importEngine, ComposablePart part)
            {
                this._importEngine = importEngine;
                this._part = part;
            }

            public ComposablePart Part
            {
                get
                {
                    return this._part;
                }
            }

            public ImportState State
            {
                get
                {
                    using (this._importEngine._lock.LockStateForRead())
                    {
                        return this._state;
                    }
                }
                set
                {
                    using (this._importEngine._lock.LockStateForWrite())
                    {
                        this._state = value;
                    }
                }
            }

            public bool TrackingImports { get; set; }

            public IEnumerable<string> GetImportedContractNames()
            {
                if (this.Part == null)
                {
                    return Enumerable.Empty<string>();
                }

                if (this._importedContractNames == null)
                {
                    this._importedContractNames = this.Part.ImportDefinitions.Select(import => import.ContractName ?? ImportDefinition.EmptyContractName).Distinct().ToArray();
                }
                return this._importedContractNames;
            }

            public CompositionResult TrySetImport(ImportDefinition import, IEnumerable<Export> exports)
            {
                try
                {
                    this.Part.SetImport(import, exports);
                    UpdateDisposableDependencies(import, exports);
                    return CompositionResult.SucceededResult;
                }
                catch (CompositionException ex)
                {   // Pulling on one of the exports failed

                    return new CompositionResult(
                        ErrorBuilder.CreatePartCannotSetImport(Part, import, ex));
                }
                catch (ComposablePartException ex)
                {   // Type mismatch between export and import

                    return new CompositionResult(
                        ErrorBuilder.CreatePartCannotSetImport(Part, import, ex));
                }
            }

            public void SetSavedImport(ImportDefinition import, Export[] exports, AtomicComposition atomicComposition)
            {
                if (atomicComposition != null)
                {
                    var savedExports = this.GetSavedImport(import);

                    // Add a revert action to revert the stored exports
                    // in the case that this atomicComposition gets rolled back.
                    atomicComposition.AddRevertAction(() =>
                        this.SetSavedImport(import, savedExports, null));
                }

                if (this._importCache == null)
                {
                    this._importCache = new Dictionary<ImportDefinition, Export[]>();
                }

                this._importCache[import] = exports;
            }

            public Export[] GetSavedImport(ImportDefinition import)
            {
                Export[] exports = null;
                if (this._importCache != null)
                {
                    // We don't care about the return value we just want the exports
                    // and if it isn't present we just return the initialized null value
                    this._importCache.TryGetValue(import, out exports);
                }
                return exports;
            }

            public void ClearSavedImports()
            {
                this._importCache = null;
            }

            public CompositionResult TryOnComposed()
            {
                try
                {
                    this.Part.Activate();
                    return CompositionResult.SucceededResult;
                }
                catch (ComposablePartException ex)
                {   // Type failed to be constructed, imports could not be set, etc
                    return new CompositionResult(
                        ErrorBuilder.CreatePartCannotActivate(this.Part, ex));
                }
            }

            public void UpdateDisposableDependencies(ImportDefinition import, IEnumerable<Export> exports)
            {
                // Determine if there are any new disposable exports, optimizing for the most
                // likely case, which is that there aren't any
                List<IDisposable> disposableExports = null;
                foreach (var disposableExport in exports.OfType<IDisposable>())
                {
                    if (disposableExports == null)
                    {
                        disposableExports = new List<IDisposable>();
                    }
                    disposableExports.Add(disposableExport);
                }

                // Dispose any existing references previously set on this import
                List<IDisposable> oldDisposableExports = null;
                if (this._importedDisposableExports != null &&
                    this._importedDisposableExports.TryGetValue(import, out oldDisposableExports))
                {
                    oldDisposableExports.ForEach(disposable => disposable.Dispose());

                    // If there aren't any replacements, get rid of the old storage
                    if (disposableExports == null)
                    {
                        this._importedDisposableExports.Remove(import);
                        if (!this._importedDisposableExports.FastAny())
                        {
                            this._importedDisposableExports = null;
                        }

                        return;
                    }
                }

                // Record the new collection
                if (disposableExports != null)
                {
                    if (this._importedDisposableExports == null)
                    {
                        this._importedDisposableExports = new Dictionary<ImportDefinition, List<IDisposable>>();
                    }
                    this._importedDisposableExports[import] = disposableExports;
                }
            }

            public void DisposeAllDependencies()
            {
                if (this._importedDisposableExports != null)
                {
                    IEnumerable<IDisposable> dependencies = this._importedDisposableExports.Values
                        .SelectMany(exports => exports);

                    this._importedDisposableExports = null;

                    dependencies.ForEach(disposableExport => disposableExport.Dispose());
                }
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) http://www.ansibleww.com.au
Australia Australia
The Australia born French man who went back to Australia later in life...
Finally got over life long (and mostly hopeless usually, yay!) chronic sicknesses.
Worked in Sydney, Brisbane, Darwin, Billinudgel, Darwin and Melbourne.

Comments and Discussions