Archive

Archive for the ‘MVVM’ Category

Simple, Blendable, DI driven ViewModels

November 19, 2010 3 comments

I hope you won’t think less of me, but I need to admit something: Dependency Injection still confuses the heck out of me.  The first time I met my good friend Kevin Hazzard he was talking about Castle Windsor with someone at an after party for a Microsoft event in Washington, D.C.  I was new to the community scene and had never met Kevin or most of the people in the room.  Wanting to fit in with the crowd I made the mistake of asking “what’s Castle Windsor?” which soon led to me asking “What’s Dependency Injection?” which even sooner led to my eyes glazing over and my brain retreating to its happy place.  I spent the next hour bobbing my head up and down, pretending to keep up with the conversation, but truthfully I was completely lost.

In the years since I’ve tried to learn DI: I’ve read articles, downloaded samples, gone to presentations, and had conversations until I was blue in the face.  I’ve made progress but I am still not totally comfortable with the whole idea.  Don’t get me wrong: I believe there is value there, and I have used it successfully in a couple of projects, but there is no guarantee I’ve done it properly.  It still feels like Black Magic but I figure if I keep plugging away it will eventually sink in.

On a side note, isn’t “Dependency Injection” a terrible name?  Almost as bad as “Inversion of Control”.  Neither one of these really describes what’s happening.  It sounds like what we are doing is injecting dependencies, meaning actually inserting dependencies, which would create more dependencies!  Really, we are resolving dependencies, or injecting dependent objects, but I guess Dependent Object Resolution is a little long winded.

The ViewModelLocator Pattern

I watched the video of John Papa’s PDC10 presentation Kung Fu Silverlight: Architectural Patterns and Practices with MVVM and RIA Services with great interest.  I’m working primarily in Silverlight now, using RIA Services and of course MVVM.  I downloaded the source code and have been working through it primarily focused on the ViewModelLocator pattern.

I’ve been aware of the pattern for a while but never understood how it worked before, so I wanted to give it a try.  The approach certainly works but it felt heavy and confusing, I’m sure that would pass as I grow more familiar with the pattern. Then again, it could just be me, but it seems there are lots of moving parts and misdirection.  Let me see if I can map it out for you as I understand it:

ServiceProviderBase: this is an abstract class that holds a static instance of the active ServiceProvider.

– A ServiceProvider is a class that inherits from ServiceProviderBase that has a reference to a Data Service instance (the class that manages all the data interactions via RIA services).  For this exercise there are two Service Providers, one for Design time and one for Run time.  It’s a little confusing, but the run time class is simply named ServiceProvider.  The design time class is called DesignServiceProvider.

Let me stop here for a second: ServiceProviderBase is inherited by the two Service Provider classes, and it’s primary function is to decide which class to instantiate and return from a static method, which upcasts the results to itself:

public abstract class ServiceProviderBase
{
    public virtual IPageConductor PageConductor { get; protected set; }
    public virtual IBookDataService BookDataService { get; protected set; }

    private static ServiceProviderBase _instance;
    public static ServiceProviderBase Instance
    {
        get { return _instance ?? CreateInstance(); }
    }

    static ServiceProviderBase CreateInstance()
    {
        // TODO:  Uncomment
        return _instance = DesignerProperties.IsInDesignTool ?
            (ServiceProviderBase)new DesignServiceProvider() : new ServiceProvider();

        // TODO:  Comment
        // return _instance = new ServiceProvider();
    }
}

This strikes me as convoluted, but let’s move on.

– The ViewModelLocator, the namesake of the pattern, is a class that holds a set of ViewModel properties.  These ViewModels require (or have a dependency on) the Data Services that are stored in the ServiceProvider instance returned from ServiceProviderBase.  When a ViewModel is requested from the ViewModelLocator, it uses ServiceProviderBase to retrieve the current Data Service and uses it in the constructor to create the ViewModel.  This is a form of Dependency Injection called Constructor Injection and is the most popular by far since many people use it without realizing they are using DI!

Here is the code from the sample:

public class ViewModelLocator
{
    private readonly ServiceProviderBase _sp;

    public ViewModelLocator()
    {
        _sp = ServiceProviderBase.Instance;

        // 1 VM for all places that use it. Just an option
        Book = new BookViewModel(_sp.PageConductor, _sp.BookDataService);
    }

    public BookViewModel Book { get; set; }
    //get { return new BookViewModel(_sp.PageConductor, _sp.BookDataService); }

    // 1 new instance per View
    public CheckoutViewModel Checkout
    {
        get { return new CheckoutViewModel(_sp.PageConductor, _sp.BookDataService); }
    }
}

This is more understandable, like a central repository of ViewModels for the entire application.  This approach assumes that they all use the same Data Service, but I’m totally cool with that because it is extremely likely.  What’s nice here is that the ViewModel is always the same regardless of which Service Provider is currently active.  The bad news is that it breaks some aspects of Blendability because there is no empty constructor, but more on that later.

– Now we need to access the ViewModelLocator, so create a ResourceDictionary that contains an instance declaration of the locator and add a reference to the dictionary in the App.xaml MergedDictionaries section.

<local:ViewModelLocator x:Key="Locator" />
...
<ResourceDictionary Source="Assets/ObjectResourceDictionary.xaml"/>

– Finally, bind the UserControl’s DatContext to the correct ViewModel property in the ViewModelLocator.

DataContext="{Binding Book, Source={StaticResource Locator}}"

If you are confused after reading the above, don’t worry because you are not alone.  I struggled for some time to sort this out and I’m not entirely convinced I ever totally got it right.

 

What I came up with

I know it’s easy to play desk chair quarterback, so before I begin let me say that it took people a heck of a lot smarter than me to come up with this and it works, so if you are already successfully doing this I’m not telling you to switch. 

Trying to grok what was going on, I just kept staring at it and thinking there should be a cleaner way, so I played with it until I came up with what I’ll share in the next section.  It’s quite possible that my approach has serious problems, so please feel free to leave comments below.

I basically set out to do two things: first, I prefer to see the DataSource listed in the Data tab in Blend.  Using the above approach you have to add the reference to the ViewModelLocator StaticResource defined in the ObjectResourceDictionary manually. The idea is that the ViewModelLocator acts as a Singleton, because there is only one instance created for the entire application.  Unless you NEED to enforce a Singleton, then this is not necessary in my mind. 

Because it is a Static Resource, the bound DataContext object will not show up as a Data Source in the Data tab. It will, however, show the appropriate properties in the DataContext panel on the bottom of the Data tab, which may be sufficient for you.

If you want to use the Data tab in Blend, however, you can remove the UserControl DataContext reference and create a local instance of the ViewModelLocator object using the standard Create Object Data Source tool in Blend.  Then you can drag the appropriate ViewModel property and drop it on the LayoutRoot to bind it to the DataContext.

The result of the second approach is that I can see ALL the ViewModel objects, which I may or may not want.  It also requires an extra level of nesting to get to the desired ViewModel object.  To get back to the more traditional Blend approach, we need to be able to bind directly to a local instance of the ViewModel itself.  Of course, doing so breaks the ViewModelLocator pattern, but I’m no purist. 🙂

 

The Non-locator Locator Pattern

My solution does away with the ServiceProviderBase and ViewModelLocator classes.  My reasoning is pretty straight forward: all I really need to be able to do is change what Data Service class my ViewModel uses based on certain scenarios.  I want a dummy service for design time but the real deal to execute at run time.  And I may want to create a special service for testing scenarios.  Getting back to how I started this post, this sounds like a job for Dependency Injection!

Since I’m writing a Silverlight application I need to make sure the IoC container I choose supports it.  I’ve been using StructureMap but it doesn’t support Silverlight yet (rumor has it V3 will add Silverlight support). Unity 2.0 for Silverlight does however, so I’m using this project as an excuse to try it out.  It shouldn’t matter what framework you use.

I added the following code to the Application_Startup method in App.xaml.cs:

var iocContainer = new UnityContainer();
if (DesignerProperties.IsInDesignTool)
{
    iocContainer.RegisterType<IClientService, ClientServiceMockData>();
}
else
{
    iocContainer.RegisterType<IClientService, ClientService>();
}
Resources.Add("IocContainer", iocContainer);

This creates a UnityContainer and registers the appropriate Data Service.  I then add the container as an Application level resource, so I can retrieve it from anywhere in the application.  This uses the same DesignerProperties. IsInDesignTool approach that the previous ViewModelLocator used.  Now we head to the ViewModel itself.

The ViewModel

In the ViewModel I add a property for IClientService with a backing field.  In the get block, if the backing field is null, I access the IocContainer application resource and use that to extract the IClientService

private IClientService _clientService;
protected IClientService ClientService
{
    get
    {
        if (_clientService == null)
        {
            var ioc = Application.Current.Resources["IocContainer"] as UnityContainer;
            _clientService = ioc == null
                ? new ClientServiceMockData()
                : ioc.Resolve<IClientService>();
        }
        return _clientService;
    }
    set { _clientService = value; }
}

NOTE: I do want to share a problem I had here: ideally, ioc.Resolve<IClientService>() should work for both Design and Run time.  This solution works as desired at run time, but at design time the IocContainer resource is null.  To solve this and get run time data I hardcoded the creation of ClientServiceMockData class if the resource is null.  This of course adds a dependency on the ClientServiceMockData class, so if you have any suggestions on how to solve this problem I would appreciate hearing them.

This approach is probably wrong somehow, but I’m accessing the IoC container from inside the object that has the dependency.  I suppose this adds a dependency on the container class itself.  I did it this way to complete the Blendability.  In order for Blend to recognize the ViewModel as an Object Data Source at run time it has to have an empty constructor, so the ViewModel now has two constructors:

public ClientViewModel()
{
    LoadData();
}

public ClientViewModel(IClientService clientService)
{
    ClientService = clientService;
    LoadData();
}

You can see that the second constructor still allows Constructor Injection, so if you wanted to use special data for testing all you would have to do is pass in a specific Data Service to the constructor.

At this point we now have a completely Blendable ViewModel that supports design time data in far fewer objects and steps, and as an added bonus it can increase your geek factor because it uses Dependency Injection!

 

Conclusion

It seems to me that this is a much simpler way to achieve the same result, but I’m sure the Patterns and Practices folks will be able to spot the holes in it.  I’d really like to learn this stuff better so please feel free to leave your comments below.  What is wrong about this idea?  What is good?  Is there an even simpler way to achieve it?  Let’s hash it out: enquiring minds want to know.