Architecting SL4 Applications with RIA Services, MEF and MVVM - Part 2

Architecture

If you've been following this blog, you know that earlier this week I released the first part of the series oon how to architect your Silverlight 4 projects. In this second part, I want to show you how the Managed Extensibility Framework (MEF) can aid in that process.

Refactoring

If you read the first part of this series, go get the code again as I've refactored it to make it a little easier to create testability code. Chiefly I made two important changes:

  • Renamed the Data project to Common and put all the interfaces in that project.
  • Removed the interfaces on the ViewModels

I realized that my original project layout wasn't handling the dependencies the way I really wanted it to. In the old project, I couldn't create a test project that didn't include the GamesModel class which is exactly what I wanted since I was going to mock the GamesModel in the test project.

In the comments on Part 1 of this series, Rob (he didn't leave a last name) opened my eyes on my plan to have Interfaces on the ViewModels.  I had done this to allow me to mock up the view models if I wanted to test the Views separately. He correctly mentioned that since there wasn't a hard dependency on the interface, it was unnecessary.  Since the views rely on data binding, they never care about the interface. In many ways, data binding allows us to create a contract on convention instead of a hard reference to the interface. This is a good thing.

How I am Using MEF

In MVVM, one of the controversial questions that comes in up most discussions is how to link the view and the view model. There are a number of solutions out there, but for me I wanted to have all three layers of the MVVM model link at runtime without having to bring them all together manually. This is where MEF comes in.

There is a lot about MEF to learn, but I'll focus on a couple of small pieces. First, MEF has the concept of Exports and Imports. Exports are simply a way of attributing your code to say "I create somethng that others might need". And Imports do the reverse: "I need something like this, please find it for me."  Let's see an example where I am using these concepts to link my model, model view and view together at runtime. Down in the model, I want to tell the system that am an implementation that others might need so I use the Export attribute on the class like so:

[Export(typeof(IGamesModel))]
public class GamesModel : IGamesModel

On the otherside, I want to request a model where I need one in a view model:

[Export("GameListViewModel")]
public class GameListViewModel : MyViewModelBase
{
  IGamesModel _model;

  [ImportingConstructor]
  GameListViewModel(IGamesModel theModel)

In the view model class I need an object that implements the IGamesModel interface. To cause it to be supplied for me, I use the ImportingConstructor which tells MEF to supply any parameters necessary if creating this object.

In addition to requiring the IGamesModel, this same class also exports a view model that others may need (e.g. the view). So I can tell MEF that I am available to fulfill those requests.  But unlike specifying the export using the interface, MEF allows me to use named exports as well.  Since our view model doesn't require a specific interface to be implemented, using a string to specify what object key we are fulfilling is adequate. 

In addition to constructor imports, you can also request imports at the property level.  As seen below, in our view we want to import a view model using the name "GameListViewModel" (which our view model exported if you remember):

[Import("GameListViewModel")]
public object ViewModel
{
  set
  {
    DataContext = value;
  }
}

Since this property requires an object that represents the view model, we can simply use the value when its set by MEF.  But how does that happen?

There are several ways of using MEF to fill these imports, but typically you can do this with a class called the PartInitializer. This class allows us to simply hand it an object and ask it to satisfy any imports declared:

public GameListView()
{
  InitializeComponent();

  // Use MEF To load the View Model
  PartInitializer.SatisfyImports(this);
}

The beauty in this is that when we call SatisfyImports, the full chain of dependencies are fulfilled.  For example, in this case this is the flow of events (the order is specified logically; the steps are not necessary executed in this order):

  • SatisfyImports is called on the View
  • MEF Searches the View for any imports that are required and find that it needs an object whose name is "GameListViewModel".
  • MEF Searches through the loaded assemblies for anyone who can fulfill the request and finds the GameListViewModel (since it had the Export specified).
  • To fulfill the import statement, MEF creates an instance of the ViewModel.
  • As the object is being created, MEF notices that the ViewModel uses a Constructor import to require an instance of IGamesModel.
  • To fulfill that request, it finds some class that Exports the IGamesModel type and finds our full GamesModel. It creates it and passes into the constructor for the view model.
  • Finally that view model is set using hte Import statement on the property.

This is MEF's job.  It tries to take these separate entities and fulfill the requests.  Unlike a traditional Inversion of Control container, the relationships between the imports and exports is more disconnected.

So how does the view get created?  It gets created because its specified in the XAML of the MainPage:

<UserControl ...
             xmlns:views="clr-namespace:RiaXBoxGames.Client.Views" >
  <Grid x:Name="LayoutRoot">
    <views:GameListView Width="325"/>								Margin="2" />

So the initial creation happens on the view because its created during the XAML parsing. This means our creation is simple and ignorant of the wireup. The MainPage.xaml is small and quiet.

One of the problems that MEF solved for me as well was sharing of one of these required classes.  For me I wanted to have one and only one instance of the model so I could re-use it in several places.  By adding the PartCreationPolicy attribute, I can specify that its a shared component (therefor a logical singleton):

[PartCreationPolicy(CreationPolicy.Shared)]
[Export(typeof(IGamesModel))]
public class GamesModel : IGamesModel
{

This type of loose coupling is what I craved from typical IoC containers, but since they required tedious registration of the objects it created more work and less ability to discover the items in a system. That's the key take-away for those of you who haven't touched MEF yet, it allows you to create loosely coupled applicaitons that use discovery to build themselves (instead of concrete catalogs). This could be a big deal in large enterprise applications.

What do you think?

Comments:

Gravatar

Nice Article Shawn, I've used a similar approach using PartCreator<T> on my blog http://www.davidezordan.net/blog/?p=1663, I love the idea to build loosely coupled applications which use composition to build themselves. What about using custom attributes instead of magic strings?
Thanks,
Davide

Gravatar

Great series Shawn.
Can you explain why you chose MEF rather than Prism?
Apparently Brad Abrams hasn't explicitly responded yet to your "feedback" on his RIAS PDC talk?
Thanks,
Alan Cobb

Gravatar

In most cases a ViewModel is created for one specific View, in this case 1:1 relation. So basically a base class can be used for all Views to locate the ViewModel type, using MEF etc for imports..in that case there is no need to add the ViewModel as an element to the XAML. What's your thoughts about that?

Gravatar

Davide,

Custom attributes would be great instead of magic strings...especially if you need additional data there. Otherwise, i'd probably refactor this to use a readonly string (or const) instead.

Gravatar

Alan,

This is just an alternative approach to Prism. It isn't better or word IMHO. The Prism approach is a bit more structured and may be easier to fit into a large team, but this approach is a bit less married to the configuration approach. Bother are valid.

Gravatar

Fredrik,

The approach you mention is what the locator pattern that Laurent Bugnion's MVVM Light recommends. I prefer this approach in some cases where I don't want a single point of code to find all my VM's. But see his blog for some interesting solutions.

Gravatar

Richardo,

I haven't seen that but it doesn't surprise me. You may need to disable resolution (e.g. not call SatisfyImports) during design mode. The issue is that the other assemblies aren't loaded at design time so it can't find imports.

Gravatar

I don't subscribe to the 1:1 View to ViewModel concept that Fredrik mentioned. The scenario I always use is a single master/detail view that has a DataGrid and a DataForm on it. If everything is designed correctly then I should be able to refactor that view into two separate views one having just the grid and the other having just the DataForm without having to change the ViewModel. In that scenario, the ViewModel has to be shared as the bound "SelectedItem" property is how the grid and the DataForm communicate with each other.

Gravatar

Colin,

That's similar to what I do here but of course, I tend to avoid the DataForm as I don't think its the greatest experience for users, but it does allow you to save a lot of time in building apps.

Gravatar

Shawn, now I see why so many people are confused. Unity(in PRISM) does the same thing, although as you mentioned, it's more structured, as everything revolves around a container.

Now, MEF comes alone, with features similar to an IOC, and people begin to question what to use, and where.

In this case, you could easily use either, and in your case, you could have used Unity without any attributes. I'm not one who hates attributes, but seeing your code, they do provide a very clear view of the design intention, where as the Unity approach requires you to know what's in the container(generally not a problem).

Moving forward, MS needs to clearly define what fits where, because they are putting out a lof of overlapping frameworks.

Gravatar

In general, a real IoC container is often more desireable, but MEF comes in with a different model that works in more discoverability cases. For example, an application where you want to support extensibility. The ImportMany attribute becomes really important then as it allows MEF to search for all Exports of a specific type to import into a collection. This allows for the simple inclusion of an assembly to change the way that an application works. With Unity/Prism, you must not only implement the IModule in the new assembly, but you must know to add the assembly to the catalog.

Again, for different use-cases (though many overlap) they both can do great jobs. I knwo the idea of choice is sometimes bad for developers but this is a good idea not a bad one.

Gravatar

You just completed the picture for me. :) That's exactly what I've been trying to figure out. I actually hadn't considered MEF for this. Thanks!

Gravatar

Shawn,
I'm really struggling to find the proper version of MvvmLight so that I can run this thing. It took me a while to discover that the V3 alpha is what contains the .Extras.dll. The final piece seems to be a missing import of GalaSoft.MvvmLight.Threading in RiaXBoxGames.Client\App.xaml.cs

Please help - I want to run this :)

Gravatar

Sorry I was using MvvmLight v3 Beta.

Gravatar

Shawn, choice is good, packaging two things that do the same thing in different ways tends to promote confusion, and that's what's going on here.

Why? Because you can't promote proper patterns and practices if you're not explaining the problem you're trying to solve, and the method to solve it.

This is understandably a problem MS is wrestling with, as evidenced by this article - http://codebetter.com/blogs/glenn.block/archive/2009/12/02/mef-and-prism-to-be-or-not-to-be.aspx

This is a problem, but not an earth shaking one, just something that needs to be cleared up between the two development teams, and it looks like that's happening.

Gravatar

Do you have a link? According to this [1], V3 Alpha is the latest and in that I'm not seeing the namespace GalaSoft.MvvmLight.Threading?


[1] http://www.galasoft.ch/mvvm/installing/manually/

Gravatar

Kelly,

I don't know where the Threading namespace is in the Extra's assembly (and I was wrong, it is the Alpha version). Next time I package it (for the last part of the series early next week), i'll add all the required assemblies and ship them too. Sorry for the confusion.

Gravatar

Good post Shawn, now the missing piece of the puzzle is Blendability, are you going to show it in the next episode?

Gravatar

@Fallon, the best summary of what is going on with Prism, MEF, and Unity can be found at:
http://blogs.msdn.com/dphill/archive/2009/12/09/prism-and-mef.aspx

My belief is that MEF and Unity complement each other very well. Unity is best inside a module and MEF is best at putting modules together.

Gravatar

It's good idea .
but there is better soloution for doing that .
Prism + MvvM is more Powerful and Flexible .

Gravatar

Corrado,

Yes, I will cover Blendability. As you might know this is a work in progress.

Gravatar

Saeed,

Both work well depending on your style. Unity + MEF in Silverlight is going to be more common that you might expect.

Gravatar

Hi Shawn,

I really like your article and share your ideas abour how Silverlight application should be designed. I'm creating a similar application based on your article and struggling implementing CRUD operations.

Do you plan to extend your sample with simple Add/Delete Game operations?

Thanks,
Anton

Gravatar

Hi Shawn,

thanks for the MEF intro.
Do you have a particular reason to choose MVVM light framework over Caliburn for Silverlight business apps?

Gravatar

Do you think that using this attribute is a good idea:
[PartCreationPolicy(CreationPolicy.Shared)]

It actually means that GamesModel is a singleton. I don't think that a component should decide about its usage/lifetime. What if you need need multiple instances in the future?
With Ioc you could configure lifetime of a component independently.

Gravatar

Chris,

In this case it is deciding, but with MEF you can have either the component decide or the instantiator. You aren't required to do it via the attribute.


 



 
Save Cancel