|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Composition.Convention;
using System.Composition.Debugging;
using System.Composition.Hosting.Core;
using System.Composition.TypedParts;
using System.Composition.TypedParts.Util;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
namespace System.Composition.Hosting
{
/// <summary>
/// Configures and constructs a lightweight container.
/// </summary>
[DebuggerTypeProxy(typeof(ContainerConfigurationDebuggerProxy))]
public class ContainerConfiguration
{
private AttributedModelProvider _defaultAttributeContext;
private readonly List<ExportDescriptorProvider> _addedSources = new List<ExportDescriptorProvider>();
private readonly List<Tuple<IEnumerable<Type>, AttributedModelProvider>> _types = new List<Tuple<IEnumerable<Type>, AttributedModelProvider>>();
/// <summary>
/// Create the container. The value returned from this method provides
/// the exports in the container, as well as a means to dispose the container.
/// </summary>
/// <returns>The container.</returns>
public CompositionHost CreateContainer()
{
var providers = _addedSources.ToList();
foreach (var typeSet in _types)
{
var ac = typeSet.Item2 ?? _defaultAttributeContext ?? new DirectAttributeContext();
providers.Add(new TypedPartExportDescriptorProvider(typeSet.Item1, ac));
}
return CompositionHost.CreateCompositionHost(providers.ToArray());
}
/// <summary>
/// Add an export descriptor provider to the container.
/// </summary>
/// <param name="exportDescriptorProvider">An export descriptor provider.</param>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithProvider(ExportDescriptorProvider exportDescriptorProvider)
{
if (exportDescriptorProvider is null)
{
throw new ArgumentNullException(nameof(exportDescriptorProvider));
}
_addedSources.Add(exportDescriptorProvider);
return this;
}
/// <summary>
/// Add conventions defined using a <see cref="AttributedModelProvider"/> to the container.
/// These will be used as the default conventions; types and assemblies added with a
/// specific convention will use their own.
/// </summary>
/// <param name="conventions"></param>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithDefaultConventions(AttributedModelProvider conventions)
{
if (conventions is null)
{
throw new ArgumentNullException(nameof(conventions));
}
if (_defaultAttributeContext != null)
throw new InvalidOperationException(SR.ContainerConfiguration_DefaultConventionSet);
_defaultAttributeContext = conventions;
return this;
}
/// <summary>
/// Add a part type to the container. If the part type does not have any exports it
/// will be ignored.
/// </summary>
/// <param name="partType">The part type.</param>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithPart(Type partType)
{
return WithPart(partType, null);
}
/// <summary>
/// Add a part type to the container. If the part type does not have any exports it
/// will be ignored.
/// </summary>
/// <param name="partType">The part type.</param>
/// <param name="conventions">Conventions represented by a <see cref="AttributedModelProvider"/>, or null.</param>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithPart(Type partType, AttributedModelProvider conventions)
{
if (partType == null) throw new ArgumentNullException(nameof(partType));
return WithParts(new[] { partType }, conventions);
}
/// <summary>
/// Add a part type to the container. If the part type does not have any exports it
/// will be ignored.
/// </summary>
/// <typeparam name="TPart">The part type.</typeparam>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithPart<TPart>()
{
return WithPart<TPart>(null);
}
/// <summary>
/// Add a part type to the container. If the part type does not have any exports it
/// will be ignored.
/// </summary>
/// <typeparam name="TPart">The part type.</typeparam>
/// <param name="conventions">Conventions represented by a <see cref="AttributedModelProvider"/>, or null.</param>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithPart<TPart>(AttributedModelProvider conventions)
{
return WithPart(typeof(TPart), conventions);
}
/// <summary>
/// Add part types to the container. If a part type does not have any exports it
/// will be ignored.
/// </summary>
/// <param name="partTypes">The part types.</param>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithParts(params Type[] partTypes)
{
return WithParts((IEnumerable<Type>)partTypes);
}
/// <summary>
/// Add part types to the container. If a part type does not have any exports it
/// will be ignored.
/// </summary>
/// <param name="partTypes">The part types.</param>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithParts(IEnumerable<Type> partTypes)
{
return WithParts(partTypes, null);
}
/// <summary>
/// Add part types to the container. If a part type does not have any exports it
/// will be ignored.
/// </summary>
/// <param name="partTypes">The part types.</param>
/// <param name="conventions">Conventions represented by a <see cref="AttributedModelProvider"/>, or null.</param>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithParts(IEnumerable<Type> partTypes, AttributedModelProvider conventions)
{
if (partTypes is null)
{
throw new ArgumentNullException(nameof(partTypes));
}
_types.Add(Tuple.Create(partTypes, conventions));
return this;
}
/// <summary>
/// Add part types from an assembly to the container. If a part type does not have any exports it
/// will be ignored.
/// </summary>
/// <param name="assembly">The assembly from which to add part types.</param>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithAssembly(Assembly assembly)
{
return WithAssembly(assembly, null);
}
/// <summary>
/// Add part types from an assembly to the container. If a part type does not have any exports it
/// will be ignored.
/// </summary>
/// <param name="assembly">The assembly from which to add part types.</param>
/// <param name="conventions">Conventions represented by a <see cref="AttributedModelProvider"/>, or null.</param>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithAssembly(Assembly assembly, AttributedModelProvider conventions)
{
return WithAssemblies(new[] { assembly }, conventions);
}
/// <summary>
/// Add part types from a list of assemblies to the container. If a part type does not have any exports it
/// will be ignored.
/// </summary>
/// <param name="assemblies">Assemblies containing part types.</param>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithAssemblies(IEnumerable<Assembly> assemblies)
{
return WithAssemblies(assemblies, null);
}
/// <summary>
/// Add part types from a list of assemblies to the container. If a part type does not have any exports it
/// will be ignored.
/// </summary>
/// <param name="assemblies">Assemblies containing part types.</param>
/// <param name="conventions">Conventions represented by a <see cref="AttributedModelProvider"/>, or null.</param>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithAssemblies(IEnumerable<Assembly> assemblies, AttributedModelProvider conventions)
{
if (assemblies is null)
{
throw new ArgumentNullException(nameof(assemblies));
}
return WithParts(assemblies.SelectMany(a => a.DefinedTypes.Select(dt => dt.AsType())), conventions);
}
/// <summary>
/// Add a single instance to the container.
/// </summary>
/// <typeparam name="TExport">The type of the contract of the instance.</typeparam>
/// <param name="exportedInstance">The instance to add to the container.</param>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithExport<TExport>(TExport exportedInstance)
{
if (exportedInstance is null)
{
throw new ArgumentNullException(nameof(exportedInstance));
}
return WithExport(exportedInstance, null, null);
}
/// <summary>
/// Add a single instance to the container.
/// </summary>
/// <typeparam name="TExport">The type of the contract of the instance.</typeparam>
/// <param name="exportedInstance">The instance to add to the container.</param>
/// <param name="contractName">Optionally, a name that discriminates this contract from others with the same type.</param>
/// <param name="metadata">Optionally, a non-empty collection of named constraints that apply to the contract.</param>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithExport<TExport>(TExport exportedInstance, string contractName = null, IDictionary<string, object> metadata = null)
{
if (exportedInstance is null)
{
throw new ArgumentNullException(nameof(exportedInstance));
}
return WithExport(typeof(TExport), exportedInstance, contractName, metadata);
}
/// <summary>
/// Add a single instance to the container.
/// </summary>
/// <param name="contractType">The type of the contract of the instance.</param>
/// <param name="exportedInstance">The instance to add to the container.</param>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithExport(Type contractType, object exportedInstance)
{
if (contractType is null)
{
throw new ArgumentNullException(nameof(contractType));
}
if (exportedInstance is null)
{
throw new ArgumentNullException(nameof(exportedInstance));
}
return WithExport(contractType, exportedInstance, null, null);
}
/// <summary>
/// Add a single instance to the container.
/// </summary>
/// <param name="contractType">The type of the contract of the instance.</param>
/// <param name="exportedInstance">The instance to add to the container.</param>
/// <param name="contractName">Optionally, a name that discriminates this contract from others with the same type.</param>
/// <param name="metadata">Optionally, a non-empty collection of named constraints that apply to the contract.</param>
/// <returns>A configuration object allowing configuration to continue.</returns>
public ContainerConfiguration WithExport(Type contractType, object exportedInstance, string contractName = null, IDictionary<string, object> metadata = null)
{
if (contractType is null)
{
throw new ArgumentNullException(nameof(contractType));
}
if (exportedInstance is null)
{
throw new ArgumentNullException(nameof(exportedInstance));
}
return WithProvider(new InstanceExportDescriptorProvider(exportedInstance, contractType, contractName, metadata));
}
internal ExportDescriptorProvider[] DebugGetAddedExportDescriptorProviders()
{
return _addedSources.ToArray();
}
internal Tuple<IEnumerable<Type>, AttributedModelProvider>[] DebugGetRegisteredTypes()
{
return _types.ToArray();
}
internal AttributedModelProvider DebugGetDefaultAttributeContext()
{
return _defaultAttributeContext;
}
}
}
|