Search code examples
c#.netmvpmef

MEF, creating Import's tree when a branch is requested


Using MEF I want to do the following.

I have a WPF Shell. To the shell I want to Import from another DLL a UserControl that is also a View of my MVP triad. The way the MVP triad works, is that in presenter I have a constructor that takes both IModel and IView and wires them up. So, in order for this to work, I need MEF to do the following:

  1. Create IView implementation
  2. Create IModel implementation
  3. Create Presenter and pass IModel and IView to its constructor
  4. Import IView implementation into my shell when it gets displayed

Instead what it does, is it only creates the type Exporting IView and passes it to the shell, basically skipping steps 2 and 3. Its pretty logical, when you think about it, but how can I tell MEF to also create the whole triad when I ask for a IView. I don't need to reference Presenter nor model anywhere else in my Shell .dll so puting it as an Import as well is not an option (and it would be quite ugly anyway :).

I'm using the latest drop of MEF (Preview 2 Refresh). Anyone?

==Update==

I have found a solution and I blogged about it here:
Krzysztof Koźmic's blog - Creating tree of dependencies with MEF

However, I'd be more than happy if someone came up with a better solution.**


Solution

  • Check my answer here.

    http://codebetter.com/blogs/glenn.block/archive/2008/11/12/mvp-with-mef.aspx

    EDIT: (Added from the link, to prevent not being flagged as low quality / LOA)

       1: using System.ComponentModel.Composition;
       2: using System.Reflection;
       3: using Microsoft.VisualStudio.TestTools.UnitTesting;
       4:  
       5: namespace MVPwithMEF
       6: {
       7:     /// <summary>
       8:     /// Summary description for MVPTriadFixture
       9:     /// </summary>
      10:     [TestClass]
      11:     public class MVPTriadFixture
      12:     {
      13:         [TestMethod]
      14:         public void MVPTriadShouldBeProperlyBuilt()
      15:         {
      16:             var catalog = new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly());
      17:             var container = new CompositionContainer(catalog.CreateResolver());
      18:             var shell = container.GetExportedObject<Shell>();
      19:             Assert.IsNotNull(shell);
      20:             Assert.IsNotNull(shell.Presenter);
      21:             Assert.IsNotNull(shell.Presenter.View);
      22:             Assert.IsNotNull(shell.Presenter.Model);
      23:         }
      24:     }
      25:  
      26:     [Export]
      27:     public class Shell
      28:     {
      29:         private IPresenter _presenter = null;
      30:         
      31:         public IPresenter Presenter
      32:         {
      33:             get { return _presenter; }
      34:         }
      35:  
      36:         [ImportingConstructor]
      37:         public Shell(IPresenter presenter)
      38:         {
      39:             _presenter = presenter;
      40:         }
      41:     }
      42:  
      43:     public interface IModel
      44:     {
      45:     }
      46:  
      47:     [Export(typeof(IModel))]
      48:     public class Model : IModel
      49:     {
      50:         
      51:     }
      52:  
      53:     public interface IView
      54:     {
      55:     }
      56:  
      57:     [Export(typeof(IView))]
      58:     public class View : IView
      59:     {
      60:     }
      61:  
      62:     public interface IPresenter
      63:     {
      64:         IView View { get;}
      65:         IModel Model { get; }
      66:     }
      67:  
      68:     [Export(typeof(IPresenter))]
      69:     public class Presenter : IPresenter
      70:     {
      71:  
      72:         private IView _view;
      73:         private IModel _model;
      74:  
      75:         [ImportingConstructor]
      76:         public Presenter(IView view, IModel model)
      77:         {
      78:             _view = view;
      79:             _model = model;
      80:         }
      81:  
      82:         public IView View
      83:         {
      84:             get { return _view; }
      85:         }
      86:  
      87:         public IModel Model
      88:         {
      89:             get { return _model; }
      90:         }
      91:  
      92:     }
      93: }
    

    So what’s going on here?

    Shell gets injected with Presenter. Presenter gets injected with View and Model. Everything here is singletons, but doesn’t have to be.

    The difference between our two examples is that the Presenter is getting injected into the shell rather than the View. If the Presenter is creating the View then you can’t just grab the View first (as he was doing), or the Presenter will not get created. Well you can do it, but you end up hacking it to bits. Cleaner is to just inject the Presenter and have it expose an IView. We did this in Prism and it worked quite well.