michael
michael

Reputation: 15282

PRISM + MEF -- How to specify which export to use?

Basically, how can I specify which of my implementations to choose from?

FooService.cs:

public interface IFooService
{
    Int32 GetFoo();
}

[Export(typeof(IFooService))]
public sealed class Foo100 : IFooService
{
    public Int32 GetFoo()
    {
        return 100;
    }
}


[Export(typeof(IFooService))]
public sealed class Foo200 : IFooService
{
    public Int32 GetFoo()
    {
        return 200;
    }
}

ClientViewModel.cs:

[Export()]
public class ClientViewModel : NotificationObject
{
    [Import()]
    private IFooService FooSvc { get; set; }

    public Int32 FooNumber
    {
        get { return FooSvc.GetFoo(); }
    }
}

Boostrapper.cs:

public sealed class ClientBootstrapper : MefBootstrapper
{
    protected override void ConfigureAggregateCatalog()
    {
        base.ConfigureAggregateCatalog();

        //Add the executing assembly to the catalog.
        AggregateCatalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
    }

    protected override DependencyObject CreateShell()
    {
        return Container.GetExportedValue<ClientShell>();
    }

    protected override void InitializeShell()
    {
        base.InitializeShell();

        Application.Current.MainWindow = (Window)Shell;
        Application.Current.MainWindow.Show();
    }
}

ClientShell.xaml.cs:

[Export()]
public partial class ClientShell : Window
{
    [Import()]
    public ClientViewModel ViewModel
    {
        get
        {
            return DataContext as ClientViewModel;
        }
        private set
        {
            DataContext = value;
        }
    }

    public ClientShell()
    {
        InitializeComponent();
    }
}

I'm not sure where to go from here for setting up my app to inject the correct one (in this case, I want Foo100 to be injected. I know I can just let them export as themselves and specify a Foo100 instead of an IFooService, but is that the correct way?

Upvotes: 9

Views: 2509

Answers (3)

Pavlo Glazkov
Pavlo Glazkov

Reputation: 20746

If there are more than one exports with a certain contract then you will have to either import them all (by declaring a property of a collection type with the ImportMany attribute on it) or make the contracts more specific by specifying a name for the contract:

[Export("Foo100", typeof(IFooService))]
public sealed class Foo100 : IFooService
{
    public Int32 GetFoo()
    {
        return 100;
    }
}


[Export("Foo200", typeof(IFooService))]
public sealed class Foo200 : IFooService
{
    public Int32 GetFoo()
    {
        return 200;
    }
}

-

[Import("Foo100", typeof(IFooService)]
private IFooService FooSvc { get; set; }

Upvotes: 7

Aaron McIver
Aaron McIver

Reputation: 24713

I'm not familiar with MEF so this may be slightly off base however using Prism and Unity as the DI container you can specify the association via the RegisterType method. When you have multiple concrete types implementing the single interface you can associate a name with them for distinction.

IUnityContainer.RegisterType<IFooService, Foo100>("Foo100");
IUnityContainer.RegisterType<IFooService, Foo200>("Foo200");

Then when you want to resolve the given instance you can do...

IUnityContainer.Resolve<IFooService>("Foo200");

Upvotes: 1

dthorpe
dthorpe

Reputation: 36082

I don't think you can specify in MEF that you want your singleton import bound only to the Foo200 implementation.

You can either declare your import property as an IEnumerable and then you can enumerate through the selections to pick out which one you want to use in your own code, or you can make sure that the two implementations of IFooSvc reside in different assemblies and that only one of those assemblies is included when you're defining your MEF catalog.

Upvotes: 3

Related Questions