Skip to content

System.Composition (MEF2) resolves a Shared part with cyclic dependencies wrong #21284

Open
@jbe2277

Description

@jbe2277

The following code sample shows two types MyController and MyViewModel which have a cyclic dependency to each other (decoupled via the IBaz interface). Both types are Shared. The test code at the bottom gets MyController via IFoo and IBaz export.

I expect that foo and baz reference the same MyController instance because it is shared. Unfortunately, MEF2 creates two instances of MyController. That's a bug.

MEF1 behaves correct - see second code snippet.

// MEF2 Issue:
// C# Console (.NET Framework) and NuGet package "System.Composition" 1.0.31

using System;
using System.Composition;
using System.Composition.Hosting;
using System.Reflection;

public interface IFoo { }
public interface IBaz { }

[Export(typeof(IFoo)), Export(typeof(IBaz)), Shared]
public class MyController : IFoo, IBaz
{
    [ImportingConstructor]
    public MyController(Lazy<MyViewModel> myViewModel)
    {
    }
}

[Export, Shared]
public class MyViewModel
{
    [ImportingConstructor]
    public MyViewModel(IBaz baz)
    {
    }
}

internal static class Program
{
    internal static void Main()
    {
        var container = new ContainerConfiguration()
                .WithAssembly(typeof(MyController).GetTypeInfo().Assembly).CreateContainer();
        var foo = container.GetExport<IFoo>();
        var baz = container.GetExport<IBaz>();
        bool areSame = ReferenceEquals(foo, baz);
        Console.WriteLine(areSame);                         // Output: false
    }
}

The second code sample shows the same scenario with MEF1. The behavior here is correct.

// MEF1 behaves correct
// C# Console (.NET Framework) and reference "System.ComponentModel.Composition"

using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

public interface IFoo { }
public interface IBaz { }

[Export(typeof(IFoo)), Export(typeof(IBaz)), PartCreationPolicy(CreationPolicy.Shared)]
public class MyController : IFoo, IBaz
{
    [ImportingConstructor]
    public MyController(Lazy<MyViewModel> myViewModel)
    {
    }
}

[Export, PartCreationPolicy(CreationPolicy.Shared)]
public class MyViewModel
{
    [ImportingConstructor]
    public MyViewModel(IBaz baz)
    {
    }
}

internal static class Program
{
    internal static void Main()
    {
        var container = new CompositionContainer(
                new AssemblyCatalog(typeof(MyController).Assembly),
                CompositionOptions.DisableSilentRejection);
        var foo = container.GetExportedValue<IFoo>();
        var baz = container.GetExportedValue<IBaz>();
        bool areSame = ReferenceEquals(foo, baz);
        Console.WriteLine(areSame);                         // Output: true
    }
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions