diff --git a/src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj b/src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj index c1e5ea140663c..18a65ddf10137 100644 --- a/src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj +++ b/src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj @@ -18,6 +18,8 @@ System.Composition.Hosting.ContainerConfiguration + + diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/Hosting/ContainerConfiguration.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/Hosting/ContainerConfiguration.cs index 9c2f37764aab2..7db5b2c541a5e 100644 --- a/src/libraries/System.Composition.TypedParts/src/System/Composition/Hosting/ContainerConfiguration.cs +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/Hosting/ContainerConfiguration.cs @@ -197,6 +197,54 @@ public ContainerConfiguration WithAssemblies(IEnumerable assemblies!!, return WithParts(assemblies.SelectMany(a => a.DefinedTypes.Select(dt => dt.AsType())), conventions); } + /// + /// Add a single instance to the container. + /// + /// The type of the contract of the instance. + /// The instance to add to the container. + /// A configuration object allowing configuration to continue. + public ContainerConfiguration WithExport(TExport exportedInstance!!) + { + return WithExport(exportedInstance, null, null); + } + + /// + /// Add a single instance to the container. + /// + /// The type of the contract of the instance. + /// The instance to add to the container. + /// Optionally, a name that discriminates this contract from others with the same type. + /// Optionally, a non-empty collection of named constraints that apply to the contract. + /// A configuration object allowing configuration to continue. + public ContainerConfiguration WithExport(TExport exportedInstance!!, string contractName = null, IDictionary metadata = null) + { + return WithExport(typeof(TExport), exportedInstance, contractName, metadata); + } + + /// + /// Add a single instance to the container. + /// + /// The type of the contract of the instance. + /// The instance to add to the container. + /// A configuration object allowing configuration to continue. + public ContainerConfiguration WithExport(Type contractType!!, object exportedInstance!!) + { + return WithExport(contractType, exportedInstance, null, null); + } + + /// + /// Add a single instance to the container. + /// + /// The type of the contract of the instance. + /// The instance to add to the container. + /// Optionally, a name that discriminates this contract from others with the same type. + /// Optionally, a non-empty collection of named constraints that apply to the contract. + /// A configuration object allowing configuration to continue. + public ContainerConfiguration WithExport(Type contractType!!, object exportedInstance!!, string contractName = null, IDictionary metadata = null) + { + return WithProvider(new InstanceExportDescriptorProvider(exportedInstance, contractType, contractName, metadata)); + } + internal ExportDescriptorProvider[] DebugGetAddedExportDescriptorProviders() { return _addedSources.ToArray(); diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/Hosting/InstanceExportDescriptorProvider.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/Hosting/InstanceExportDescriptorProvider.cs new file mode 100644 index 0000000000000..5a7122bd3217d --- /dev/null +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/Hosting/InstanceExportDescriptorProvider.cs @@ -0,0 +1,26 @@ +// 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.Hosting.Core; + +namespace System.Composition.Hosting +{ + internal class InstanceExportDescriptorProvider : SinglePartExportDescriptorProvider + { + private readonly object _exportedInstance; + + public InstanceExportDescriptorProvider(object exportedInstance, Type contractType, string contractName, IDictionary metadata) + : base(contractType, contractName, metadata) + { + _exportedInstance = exportedInstance; + } + + public override IEnumerable GetExportDescriptors(CompositionContract contract, DependencyAccessor descriptorAccessor) + { + if (IsSupportedContract(contract)) + yield return new ExportDescriptorPromise(contract, _exportedInstance.ToString(), true, NoDependencies, _ => + ExportDescriptor.Create((c, o) => _exportedInstance, Metadata)); + } + } +} diff --git a/src/libraries/System.Composition.TypedParts/src/System/Composition/Hosting/SinglePartExportDescriptorProvider.cs b/src/libraries/System.Composition.TypedParts/src/System/Composition/Hosting/SinglePartExportDescriptorProvider.cs new file mode 100644 index 0000000000000..f20a694151d26 --- /dev/null +++ b/src/libraries/System.Composition.TypedParts/src/System/Composition/Hosting/SinglePartExportDescriptorProvider.cs @@ -0,0 +1,43 @@ +// 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.Hosting.Core; +using System.Linq; + +namespace System.Composition.Hosting +{ + internal abstract class SinglePartExportDescriptorProvider : ExportDescriptorProvider + { + private readonly Type _contractType; + private readonly string _contractName; + + protected SinglePartExportDescriptorProvider(Type contractType, string contractName, IDictionary metadata) + { + _contractType = contractType; + _contractName = contractName; + Metadata = metadata ?? new Dictionary(); + } + + protected bool IsSupportedContract(CompositionContract contract) + { + if (contract.ContractType != _contractType || + contract.ContractName != _contractName) + return false; + + if (contract.MetadataConstraints != null) + { + var subsetOfConstraints = contract.MetadataConstraints.Where(c => Metadata.ContainsKey(c.Key)).ToDictionary(c => c.Key, c => Metadata[c.Key]); + var constrainedSubset = new CompositionContract(contract.ContractType, contract.ContractName, + subsetOfConstraints.Count == 0 ? null : subsetOfConstraints); + + if (!contract.Equals(constrainedSubset)) + return false; + } + + return true; + } + + protected IDictionary Metadata { get; } + } +} diff --git a/src/libraries/System.Composition.TypedParts/tests/ContainerConfigurationTests.cs b/src/libraries/System.Composition.TypedParts/tests/ContainerConfigurationTests.cs index 7e1d7bf6cf78e..4c9e9253bdcee 100644 --- a/src/libraries/System.Composition.TypedParts/tests/ContainerConfigurationTests.cs +++ b/src/libraries/System.Composition.TypedParts/tests/ContainerConfigurationTests.cs @@ -254,6 +254,42 @@ public void WithAssemby_Null_ThrowsNullReferenceExceptionOnCreation() Assert.Throws(() => configuration.CreateContainer()); } + [Fact] + public void WithExport_Base_Success() + { + var instance = new Base(); + + var configuration = new ContainerConfiguration(); + Assert.Same(configuration, configuration.WithExport(instance)); + + CompositionHost container = configuration.CreateContainer(); + Assert.Same(instance, container.GetExport()); + } + + [Fact] + public void WithExport_Derived_Success() + { + var instance = new Derived(); + + var configuration = new ContainerConfiguration(); + Assert.Same(configuration, configuration.WithExport(instance)); + + CompositionHost container = configuration.CreateContainer(); + Assert.Same(instance, container.GetExport()); + } + + [Fact] + public void WithExport_ContractName_Success() + { + var instance = new Base(); + + var configuration = new ContainerConfiguration(); + Assert.Same(configuration, configuration.WithExport(instance, "Contract")); + + CompositionHost container = configuration.CreateContainer(); + Assert.Same(instance, container.GetExport("Contract")); + } + [Fact] public void CreateContainer_ExportedSubClass_Success() {