Type Initialization

Dec 30, 2011 at 2:53 PM

I'm working on my first project using MEF.

I have a task to re-write my non-MEF module with the MEF.

My class receives some external data (say from DB) and based on this data creates collection of ViewModels.  While creating instances of this ViewModel I need to initialize it with the DB data. In the normal situation I would pass this data via constructor parameters but as far as I understand MEF doesn't support constructor initialization.

Obviously I can create an instance of my ViewModel and then initialize it via properties but something tells me there should be a better way.

I heard somewhere that the right MEF way of doing this is to pull data from the ViewModel (in my case) instead of pushing it via constructor but I have troubles understanding where I can pull my data from because only class that creates ViewModel collection knows about this data.

I'm looking for the answer for the last couple of day and I surprised I couldn't find it. That make me think I missed something important in the MEF ideology.  All the answers I found recommend using:

ComposeExportedValue("Data", MyData)
and then use [ImportingConstructor] that reads Data from the catalog. But in my case, when I need to initialize a group of objects, this approach will not work (or I don't know how to make it works).

http://stackoverflow.com/questions/2941158/mef-constructor-parameters-with-multiple-constructors
http://mef.codeplex.com/discussions/251895

Thanks,
Maxim
Dec 30, 2011 at 3:11 PM

Hi mparsin,

The first thing is that MEF does support passing data via constructor. You can use ImportingConstrcutor attribute on the constructor on your ViewModel.

The second thing is that the senario you present here is calling for a pattern that use a factory to create your ViewModel. The reason you want this factory is to allow both the imported instances and the external data (loaded from the database) to be injected into your ViewModel.

The way I go about it is to have a ViewModelFactory class which will be used by the class that in the non-MEF application had the job of doing "new ViewModel(p1, p2, p3)". Inside the ViewModelFactory you will have a method to build the ViewModel.

I'll post some sample code later, but know that MEF is very capable to handle your senario.

 

Ido.

Dec 30, 2011 at 3:55 PM

Hi Ido,

Thank you for your answer!

1)

>>>The first thing is that MEF does support passing data via constructor.
>>>You can use ImportingConstrcutor attribute on the constructor on your ViewModel.

Correct me if I'm wrong, but when you define ImportingConstructor with parameters, it's a job of constructor to resolve this parameters. For shared parts it will get the instance of singleton for non-shared it will try to create a new instance. And that is not what I need.

 

2)

I tried the route you suggested. If I understand you right my ViewModelFactory should look something like:

 

    public class ViewModelFactory
    {
        public static ViewModel CreateViewModel(object data1, object data2)
        {
            var result = new ViewModel(data1, data2);
            container.ComposeParts(result);
            return result;
        }
    }


The problem I'm having with this approach is that it works first time but for some reason when my application handles the second group of viewModels the ComposeParts line throws composition exception:

1) Cannot create an instance of type '[MyViewModelType]' because a constructor could not be selected for construction. Ensure that the type either has a default constructor, or a single constructor marked with the 'System.ComponentModel.Composition.ImportingConstructorAttribute'.


I don't understand why MEF is looking for constructor - I'm trying to Compose already existent instance.

I guess MEF cannot satisfy one of the imports required by my ViewModel. But again this only happened second time I create the instance of my ViewModel.

Therefore my questions are:

  • Is my code correct?
  • Do I need to clean up after the call to ComposeParts so next time it will work with the fresh instance?
  • Yes please, some code sample would be very helpful.

 

Thanks again,
Maxim

 

Dec 30, 2011 at 7:35 PM
Edited Dec 30, 2011 at 7:36 PM

Hi,

I am assuming your ViewModel class is depend on both instances that the composition container should supply and data that is loaded from the database.

It is also important to note that is better not to use the CompositionContainer directly from your code because you are then depend on it and when you want to test your code you have to setup the container itself.

This is the code I suggest:

 

[Export]
public class ViewModelFactory {
    private readonly SomeService someSvc;
    private readonly AntoerhService anotherSvc;

    [ImportingConstructor]
    public ViewModelFactory(SomeService someSvc, AnotherService antoerhSvc) {
      this.someSvc = someSvc;
      this.anotherSvc = anotherSvc;
    }

    public ViewModel CreateViewModel(int data1, string data2) {
      return new ViewModel(someSvc, anotherSvc, data1, data2);
    }
  }

As you can see the ViewModelFactory itself is exported and has importing constructor which import some services that should be satisfied by the container.

In the class that today create the ViewModel (in your non-MEF app) you should import the ViewModelFactory as either constructor or property and then in the place that does "new ViewModel" in the non-MEF app you should call factory.CreateViewModel passing only the contextual information, which was read from the database in your case.

This way you get both the instances created by the container and passing your own specific data to each ViewModel.

 

The code you post earlier has static method which means that possibly the container you are using is not the one you want to use. Anyway, the method should not be static.

Also, avoid as much as possible to calling directly to the CompositionContainer.

 

I am also in the process of converting non-MEF app to MEF app so I can understand the issues around it.

 

I hope it helps and please keep on asking if this has not completely answer your question.

 

Ido.

Dec 30, 2011 at 8:39 PM
Edited Dec 30, 2011 at 8:41 PM

Hi,

Thank you for the answer.

I think we're getting closer to the solution.

One small problem with your code that ViewModel itself imports some dependencies, therefore I should not just "new" it.

[Export]
public class ViewModelFactory {
    private readonly SomeService someSvc;
    private readonly AntoerhService anotherSvc;

    [ImportingConstructor]
    public ViewModelFactory(SomeService someSvc, AnotherService antoerhSvc) {
      this.someSvc = someSvc;
      this.anotherSvc = anotherSvc;
    }

     [Import(AllowRecomposition = true, AllowDefault = true)]
     public ExportFactory<FieldViewModel> FieldFactory { get; set; }


    public ViewModel CreateViewModel(int data1, string data2) {
      var result = FieldFactory.CreateExport().Value;
      result.Data1 = data1;
      result.Data2 = data2;
     //Initialize everything else
     return result;
    }
  }
Do you think that is a correct change?
I still don't like that Data1 and Data2 become public read/write properties although they should be private read-only fields but I guess that's the price I have to pay to make the class testable.
Other thing that bothers me is that looking at my ViewModel class you cannot tell that it cannot operate properly without supplying data1 and data2 values.

So, making the long story short "In MEF instead of initializing types with the state via constructor parameters we should create
an exportable Factory and initialize type via dedicated Factory's method"
Sorry for my meticulousness but I really would like to get to the bottom of that problem because I encounter it all over my project.

Thanks again,
Maxim
Dec 31, 2011 at 12:27 PM
Edited Dec 31, 2011 at 3:04 PM
I think the change is not good. You should be able to construct your view model via normal ctor and should not pass values you relay on via properties.
No one said that each and every instance in MEF app should be build using the container.
The pattern I suggest here us useful when you want to build instance like your ViewModel which need instances from the container as well as local values.
Any instances require by your ViewModel will be imported by the factory. Then the factory pass the imported values together with the local values.
Do not compromise on your design due to a framework. You have hood design and you should keep it.
Please continue to share your thought, we all learn from that.
Thank you,
Ido
Dec 31, 2011 at 3:03 PM

Hi,

I would like to show you where I get the idea of creating a factory that import dependencies and pass though parameter values.

Google has a dependency injection framework called Guice. Guice and MEF have different agendas and target different things but they have large parts very close to each other (in my opinion).

Guice also come to the same problem or creating instances that need both dependencies and parameters and they create a way to handle it name Assisted Injection. The difference between their implementation and this one is that they take care of the boilerplate code we have to write by hand.

Hope it helps,

Ido.

Dec 31, 2011 at 3:17 PM

Ido,

I think I got the idea. 

I'll try to make this changes to the application and create a couple of unit tests to test my ViewModel (that was one of the primary goals of using MEF). I'll report my results/concerns here.

Thank you for your help,
Maxim

Dec 31, 2011 at 4:58 PM

Slight problem with newing up my ViewModel is that it, in its turn, creates collection of sub-viewmodels which currently import their dependencies:

If I understand correctly I have two options:

  • I can continue using "new" down to the hierarchy and pass dependencies as constructor parameters (drawback - hard to test viewmodels)
  • I can register my instance with CompositionContainer (drawback - artificial dependency on container as you mentioned before)

Have I missed a third "golden" option?

 

Thanks,
Maxim

Dec 31, 2011 at 8:09 PM
Hi,
you can use the factory pattern inside the ViewModel class just as you would in the outer class.
Just pass an InnerViewModelFactory instance to your ViewModel ctor.

Ido
>
Dec 31, 2011 at 11:20 PM

Hi,

I can, although it feels wrong.

In my case I have a hierarchy of viewModel types and only one of them requires collection of InnerViewModels. Other might require different kind of factories. Therefore ViewModelFactory CreateViewModel code become pretty ugly since it has to resolve all the types needed down to the hierarchy tree.

I might need to re-think the whole design of my classes.

Thank you,
Maxim

Jan 1, 2012 at 8:44 AM
You should have factory for each type of ViewModel.
For FieldViewModel you should have FieldViewModelFactory and for InnerViewModel you should have InnerViewModelFactory.
Don't spare on the number of classes you have, MEF makes it very easy to compose all the classes together.

Makes sense?

Ido
Jan 3, 2012 at 3:13 PM

Hi,

I've been thinking about this pattern for a couple of days as well as discussed it with my colleagues.  As a result of this discussion we come to a conclusion that method Init() doesn't look so ugly anymore (comparing to other options). :)

For a simple scenarios your ViewModelFactory (or AssistedInject) pattern is fine but for our "deep hierarchy" scenario it produces more problem than it solves. I will be more than happy if you or anybody else can dissuade me.

Using container.ComposeParts(result) in the factory method would help but as far as I understand it is considered a "hack" from MEF standpoint and is discouraged.

 

Thank you anyway,
Maxim

Jan 4, 2012 at 11:06 AM
Hi,
I think you right for your case.
At this point only sample code can either prove or dismiss the validity of this pattern.

Ido