// -----------------------------------------------------------------------
// 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());
}
}
}
}
}
|