Services – Your ViewModel Death Star

In my last blog post I referred to the Behavior as “your trusty ViewModel bazooka”. Well, if Behaviors are akin to bazookas, I guess services must be akin to something on the scale of the Death Star! Seriously, there’s no problem that can’t be solved by the use of a service. The concept is that powerful. That’s why they are the integral part of Onyx. Every problem that ever came up that I couldn’t find another solution for, services could be used to save the day.

Services?

So, what is a service? This very question got asked on Stack Overflow in response to an answer I gave to another question. Like I answered on Stack Overflow, put simply, a service is an object that provides functionality to be used by other objects. These services are generally provided to the other objects through either the Service Locator or Inversion of Control patterns. Onyx provides a generalized mechanism for this using the Service Locator pattern, hopefully with the hooks necessary to override this to use Inversion of Control instead.

In any event, services are powerful, because they are reusable pieces of code that can be easily replaced with alternative implementations. This means that at runtime they can do all the logic necessary to accomplish your task in a tightly coupled fashion, just as you would have in the old days before the ViewModel pattern using event handlers in your codebehind. Then in your tests you can replace this behavior with test doubles that provide the behavior you need for the test in a way that’s not coupled to any UI.

Message Boxes

Let’s try and make this concrete with an example. One problem that everyone new to the ViewModel design pattern asks about is how you display a MessageBox to the user in response to something that occurs in a Command handler in the ViewModel. You can’t simply call MessageBox.Show, because that’s makes the code impossible to test. This is one problem for which, although there might be alternative solutions, the best solution is to use a service.

The first thing to do when here is to define our service. Since a service needs to be replaceable, we need to use either an abstract class or an interface to define the service. We’ll stick with using an interface.

public interface IDisplayMessage
{
    void ShowMessage(string message);
}

I’ve kept this very simple for this blog post. Onyx actually defines an IDisplayMessage service that’s more complete, allowing you to fully specify all of the optional parameters that you can supply when calling MessageBox.Show, but that would be a distraction to this conversation. OK, the service is defined. Now we need to provide an implementation.

internal class DisplayMessageService : IDisplayMessage
{
    public void ShowMessage(string message)
    {
        MessageBox.Show(message);
    }
}

Again, this is a simplified implementation for the purposes of discussion here. All that our service does is delegate to MessageBox.Show, which is a tightly coupled implementation. That’s fine, because when we use the service, we won’t be tightly coupled, since we can provide a different implementation of the service. For instance, not only can we provide a test double in our unit tests, but we could also decide that instead of display a modal dialog, all such messages should be displayed in a list in a docked message window in our UI, and this change doesn’t require any modification to the ViewModels that use the service.

Next up, we have to supply the service to our ViewModel somehow. We’ll do this by adding a parameter to the constructor, which is the Inversion of Control pattern.

public Window1ViewModel(IDisplayMessage displayMessage)
{
    this.displayMessage = displayMessage;

Then in the ViewModel we can use this service to display a message.

this.displayMessage.ShowMessage(string.Join(Environment.NewLine, this.SelectedDisciples.ToArray()));

Keeping things simple, we’ll just manually supply the service when we create the ViewModel (this is often referred to as “poor man’s dependency injection” since we’re not using a container).

this.DataContext = new Window1ViewModel(new DisplayMessageService());

Keep in mind that everything we did here was the simplest possible implementation that can illustrate how to use services, and not necessarily the best or most maintainable way of doing this. I don’t want to confuse you with the extra code necessary to use a container for either service location or dependency injection, as those are architectural artifacts not necessary to the concept of using services.

So, we declared a service for displaying messages to the user, implemented a version that will display a MessageBox, provided the service to the ViewModel, and used it to display a message. We now have a ViewModel that displays a message in a way that’s decoupled from how the message is displayed and thus decoupled from the View and testable. All of this easily allowed us to solve a problem that at first look appears to be extremely difficult to implement when following the ViewModel design pattern. You’ll find nearly everything you run into that appears to be complicated to do when following the ViewModel pattern is actually easy to solve by using a service. In fact, you can find yourself going overboard here, and use services for problems that are better solved using other mechanisms. For instance, you could use a service to replace data bindings, but that wouldn’t be the correct solution to the problem. Don’t be too enticed by the power of services. Look first for alternative solutions, and if none present themselves, then look to the service as your solution. Remember the analogy from the last post, which is even more poignant this time around. You wouldn’t go duck hunting with a death star, nor should you solve most day to day problems with services. However, it’s nice to know there’s no problem you can’t solve as long as you’ve got services to back you up.

3 Comments

  • William said

    I\'m not sure I\'d use an ICustomerService instead of a CustomerModel, if that\'s what you meant, but the Model needs to be provided to your ViewModel in some fashion, which is a form of what we\'re discussing. A great example, my Model\'s are often "global" in nature and are obtained from the Application class. However, utilizing Application.Current in your ViewModel code is bad, Ray! So what I do is define an IApplication service, have my Application implement that, and inject it into the ViewModel via either Dependency Injection or Service Location. With Onyx, for example:public class MyViewModel{ private readonly MyModel model; public MyViewModel(View view) : base(view) { IApplication app = View.GetService<IApplication>(); this.model = app.MyModel; }}

  • Chris said

    Nice example. Would you handle interactions with the business layer in exactly the same way? For example, having an ICustomerService injected into the ViewModel?

Add a Comment