Skip to content

Commit 6fdd29a

Browse files
authored
Inject existing object into MEF2 (#66364)
1 parent e34e8dd commit 6fdd29a

File tree

5 files changed

+155
-0
lines changed

5 files changed

+155
-0
lines changed

src/libraries/System.Composition.TypedParts/src/System.Composition.TypedParts.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ System.Composition.Hosting.ContainerConfiguration</PackageDescription>
1818
<Compile Include="System\Composition\Debugging\ContainerConfigurationDebuggerProxy.cs" />
1919
<Compile Include="System\Composition\Debugging\DiscoveredPartDebuggerProxy.cs" />
2020
<Compile Include="System\Composition\Hosting\ContainerConfiguration.cs" />
21+
<Compile Include="System\Composition\Hosting\InstanceExportDescriptorProvider.cs" />
22+
<Compile Include="System\Composition\Hosting\SinglePartExportDescriptorProvider.cs" />
2123
<Compile Include="System\Composition\TypedParts\ActivationFeatures\ActivationFeature.cs" />
2224
<Compile Include="System\Composition\TypedParts\ActivationFeatures\DisposalFeature.cs" />
2325
<Compile Include="System\Composition\TypedParts\ActivationFeatures\LifetimeFeature.cs" />

src/libraries/System.Composition.TypedParts/src/System/Composition/Hosting/ContainerConfiguration.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,54 @@ public ContainerConfiguration WithAssemblies(IEnumerable<Assembly> assemblies!!,
197197
return WithParts(assemblies.SelectMany(a => a.DefinedTypes.Select(dt => dt.AsType())), conventions);
198198
}
199199

200+
/// <summary>
201+
/// Add a single instance to the container.
202+
/// </summary>
203+
/// <typeparam name="TExport">The type of the contract of the instance.</typeparam>
204+
/// <param name="exportedInstance">The instance to add to the container.</param>
205+
/// <returns>A configuration object allowing configuration to continue.</returns>
206+
public ContainerConfiguration WithExport<TExport>(TExport exportedInstance!!)
207+
{
208+
return WithExport(exportedInstance, null, null);
209+
}
210+
211+
/// <summary>
212+
/// Add a single instance to the container.
213+
/// </summary>
214+
/// <typeparam name="TExport">The type of the contract of the instance.</typeparam>
215+
/// <param name="exportedInstance">The instance to add to the container.</param>
216+
/// <param name="contractName">Optionally, a name that discriminates this contract from others with the same type.</param>
217+
/// <param name="metadata">Optionally, a non-empty collection of named constraints that apply to the contract.</param>
218+
/// <returns>A configuration object allowing configuration to continue.</returns>
219+
public ContainerConfiguration WithExport<TExport>(TExport exportedInstance!!, string contractName = null, IDictionary<string, object> metadata = null)
220+
{
221+
return WithExport(typeof(TExport), exportedInstance, contractName, metadata);
222+
}
223+
224+
/// <summary>
225+
/// Add a single instance to the container.
226+
/// </summary>
227+
/// <param name="contractType">The type of the contract of the instance.</param>
228+
/// <param name="exportedInstance">The instance to add to the container.</param>
229+
/// <returns>A configuration object allowing configuration to continue.</returns>
230+
public ContainerConfiguration WithExport(Type contractType!!, object exportedInstance!!)
231+
{
232+
return WithExport(contractType, exportedInstance, null, null);
233+
}
234+
235+
/// <summary>
236+
/// Add a single instance to the container.
237+
/// </summary>
238+
/// <param name="contractType">The type of the contract of the instance.</param>
239+
/// <param name="exportedInstance">The instance to add to the container.</param>
240+
/// <param name="contractName">Optionally, a name that discriminates this contract from others with the same type.</param>
241+
/// <param name="metadata">Optionally, a non-empty collection of named constraints that apply to the contract.</param>
242+
/// <returns>A configuration object allowing configuration to continue.</returns>
243+
public ContainerConfiguration WithExport(Type contractType!!, object exportedInstance!!, string contractName = null, IDictionary<string, object> metadata = null)
244+
{
245+
return WithProvider(new InstanceExportDescriptorProvider(exportedInstance, contractType, contractName, metadata));
246+
}
247+
200248
internal ExportDescriptorProvider[] DebugGetAddedExportDescriptorProviders()
201249
{
202250
return _addedSources.ToArray();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Generic;
5+
using System.Composition.Hosting.Core;
6+
7+
namespace System.Composition.Hosting
8+
{
9+
internal class InstanceExportDescriptorProvider : SinglePartExportDescriptorProvider
10+
{
11+
private readonly object _exportedInstance;
12+
13+
public InstanceExportDescriptorProvider(object exportedInstance, Type contractType, string contractName, IDictionary<string, object> metadata)
14+
: base(contractType, contractName, metadata)
15+
{
16+
_exportedInstance = exportedInstance;
17+
}
18+
19+
public override IEnumerable<ExportDescriptorPromise> GetExportDescriptors(CompositionContract contract, DependencyAccessor descriptorAccessor)
20+
{
21+
if (IsSupportedContract(contract))
22+
yield return new ExportDescriptorPromise(contract, _exportedInstance.ToString(), true, NoDependencies, _ =>
23+
ExportDescriptor.Create((c, o) => _exportedInstance, Metadata));
24+
}
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Generic;
5+
using System.Composition.Hosting.Core;
6+
using System.Linq;
7+
8+
namespace System.Composition.Hosting
9+
{
10+
internal abstract class SinglePartExportDescriptorProvider : ExportDescriptorProvider
11+
{
12+
private readonly Type _contractType;
13+
private readonly string _contractName;
14+
15+
protected SinglePartExportDescriptorProvider(Type contractType, string contractName, IDictionary<string, object> metadata)
16+
{
17+
_contractType = contractType;
18+
_contractName = contractName;
19+
Metadata = metadata ?? new Dictionary<string, object>();
20+
}
21+
22+
protected bool IsSupportedContract(CompositionContract contract)
23+
{
24+
if (contract.ContractType != _contractType ||
25+
contract.ContractName != _contractName)
26+
return false;
27+
28+
if (contract.MetadataConstraints != null)
29+
{
30+
var subsetOfConstraints = contract.MetadataConstraints.Where(c => Metadata.ContainsKey(c.Key)).ToDictionary(c => c.Key, c => Metadata[c.Key]);
31+
var constrainedSubset = new CompositionContract(contract.ContractType, contract.ContractName,
32+
subsetOfConstraints.Count == 0 ? null : subsetOfConstraints);
33+
34+
if (!contract.Equals(constrainedSubset))
35+
return false;
36+
}
37+
38+
return true;
39+
}
40+
41+
protected IDictionary<string, object> Metadata { get; }
42+
}
43+
}

src/libraries/System.Composition.TypedParts/tests/ContainerConfigurationTests.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,42 @@ public void WithAssemby_Null_ThrowsNullReferenceExceptionOnCreation()
254254
Assert.Throws<NullReferenceException>(() => configuration.CreateContainer());
255255
}
256256

257+
[Fact]
258+
public void WithExport_Base_Success()
259+
{
260+
var instance = new Base();
261+
262+
var configuration = new ContainerConfiguration();
263+
Assert.Same(configuration, configuration.WithExport<Base>(instance));
264+
265+
CompositionHost container = configuration.CreateContainer();
266+
Assert.Same(instance, container.GetExport<Base>());
267+
}
268+
269+
[Fact]
270+
public void WithExport_Derived_Success()
271+
{
272+
var instance = new Derived();
273+
274+
var configuration = new ContainerConfiguration();
275+
Assert.Same(configuration, configuration.WithExport<Base>(instance));
276+
277+
CompositionHost container = configuration.CreateContainer();
278+
Assert.Same(instance, container.GetExport<Base>());
279+
}
280+
281+
[Fact]
282+
public void WithExport_ContractName_Success()
283+
{
284+
var instance = new Base();
285+
286+
var configuration = new ContainerConfiguration();
287+
Assert.Same(configuration, configuration.WithExport<Base>(instance, "Contract"));
288+
289+
CompositionHost container = configuration.CreateContainer();
290+
Assert.Same(instance, container.GetExport<Base>("Contract"));
291+
}
292+
257293
[Fact]
258294
public void CreateContainer_ExportedSubClass_Success()
259295
{

0 commit comments

Comments
 (0)