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


Url: http://wilderminds.blob.core.windows.net/downloads/RiaXboxGames.zip

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?




Application Name WilderBlog Environment Name Production
Application Ver 1.1.0.0 Runtime Framework .NETCoreApp,Version=v1.1
App Path D:\home\site\wwwroot Runtime Version .NET Core 4.6.25211.01
Operating System Microsoft Windows 6.2.9200 Runtime Arch X86