Skip to content

Commit

Permalink
Inject existing object into MEF2 (#66364)
Browse files Browse the repository at this point in the history
  • Loading branch information
huoyaoyuan authored Mar 16, 2022
1 parent e34e8dd commit 6fdd29a
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ System.Composition.Hosting.ContainerConfiguration</PackageDescription>
<Compile Include="System\Composition\Debugging\ContainerConfigurationDebuggerProxy.cs" />
<Compile Include="System\Composition\Debugging\DiscoveredPartDebuggerProxy.cs" />
<Compile Include="System\Composition\Hosting\ContainerConfiguration.cs" />
<Compile Include="System\Composition\Hosting\InstanceExportDescriptorProvider.cs" />
<Compile Include="System\Composition\Hosting\SinglePartExportDescriptorProvider.cs" />
<Compile Include="System\Composition\TypedParts\ActivationFeatures\ActivationFeature.cs" />
<Compile Include="System\Composition\TypedParts\ActivationFeatures\DisposalFeature.cs" />
<Compile Include="System\Composition\TypedParts\ActivationFeatures\LifetimeFeature.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,54 @@ public ContainerConfiguration WithAssemblies(IEnumerable<Assembly> 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!!)
{
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)
{
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!!)
{
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)
{
return WithProvider(new InstanceExportDescriptorProvider(exportedInstance, contractType, contractName, metadata));
}

internal ExportDescriptorProvider[] DebugGetAddedExportDescriptorProviders()
{
return _addedSources.ToArray();
Expand Down
Original file line number Diff line number Diff line change
@@ -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<string, object> metadata)
: base(contractType, contractName, metadata)
{
_exportedInstance = exportedInstance;
}

public override IEnumerable<ExportDescriptorPromise> GetExportDescriptors(CompositionContract contract, DependencyAccessor descriptorAccessor)
{
if (IsSupportedContract(contract))
yield return new ExportDescriptorPromise(contract, _exportedInstance.ToString(), true, NoDependencies, _ =>
ExportDescriptor.Create((c, o) => _exportedInstance, Metadata));
}
}
}
Original file line number Diff line number Diff line change
@@ -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<string, object> metadata)
{
_contractType = contractType;
_contractName = contractName;
Metadata = metadata ?? new Dictionary<string, object>();
}

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<string, object> Metadata { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,42 @@ public void WithAssemby_Null_ThrowsNullReferenceExceptionOnCreation()
Assert.Throws<NullReferenceException>(() => configuration.CreateContainer());
}

[Fact]
public void WithExport_Base_Success()
{
var instance = new Base();

var configuration = new ContainerConfiguration();
Assert.Same(configuration, configuration.WithExport<Base>(instance));

CompositionHost container = configuration.CreateContainer();
Assert.Same(instance, container.GetExport<Base>());
}

[Fact]
public void WithExport_Derived_Success()
{
var instance = new Derived();

var configuration = new ContainerConfiguration();
Assert.Same(configuration, configuration.WithExport<Base>(instance));

CompositionHost container = configuration.CreateContainer();
Assert.Same(instance, container.GetExport<Base>());
}

[Fact]
public void WithExport_ContractName_Success()
{
var instance = new Base();

var configuration = new ContainerConfiguration();
Assert.Same(configuration, configuration.WithExport<Base>(instance, "Contract"));

CompositionHost container = configuration.CreateContainer();
Assert.Same(instance, container.GetExport<Base>("Contract"));
}

[Fact]
public void CreateContainer_ExportedSubClass_Success()
{
Expand Down

0 comments on commit 6fdd29a

Please sign in to comment.