Skip to content
Bruno Sonnino
Menu
  • Home
  • About
Menu

Restructuring your legacy code using MVVM and Dependency Injection with Unity

Posted on 11 August 2013

Introduction

In the previous article, "Testing the untestable with Fakes", I have shown how to create unit tests for the legacy code that didn't allow tests. With these tests in place we can give a step further to restructuring our code: using best practices on our project, creating code that can be easily maintainable in the future.

In this article we will restructure our code to use the MVVM pattern and Dependency injection. We will use here **Unity,**a framework created by Microsoft Patterns & Practices, for dependency injection.

Project architecture

Our project had all the code in code-behind and that makes creating tests a difficult task. Besides, that, it had a dependency on DateTime, that makes almost impossible to create reproducible tests.

The first step is to separate the model and the business rules from the view layer. This is done with the MVVM (Model-View-ViewModel) pattern. This pattern separates the presentation layer (View) from the data model (Model) using a binding layer, named ViewModel.

This design pattern makes extensive use of DataBinding in WPF/Silverlight/Windows Phone/Windows 8 and was created on 2005 by John Gossman, a Microsoft Architect on Blend team. If you want more information about this design pattern, you can check Josh Smith's article on MSDN Magazine, .

The ViewModel is a class that implements the INotifyPropertyChanged interface:

public interface INotifyPropertyChanged
{
  event PropertyChangedEventHandler PropertyChanged;
}
C#

It has just one event PropertyChanged that is activated when there is a change in a property. The Data binding mechanism present in WPF (and in other XAML platforms) subscribes this event and updates the view with no program intervention. So, all we need to do is to create a class that implements INotifyPropertyChanged and call this event when there is a change in a property to WPF update the view.

The greatest advantage is that the ViewModel is a normal class and doesn't have any dependency on the view layer. That way, we don't need to initialize a window when we test the ViewModel.

Creating the ViewModel

The first step for restructuring the program is to create the ViewModel. It is completely independent from the current project and doesn't interfere with it.

We will use the same project that we've used in the previous article. Create a folder and name it ViewModel. In it, add a new class and name it MainViewModel:

class MainViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] 
        string propertyName = null)
    {
        if (PropertyChanged != null) 
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
C#

Note that we've used the CallerMemberName attribute, from C# 5.0. It allows that the parameter propertyName from method OnPropertyChanged be filled with the name of the property that has called it, if we leave it blank. That means that if we have a property named TextData and do something like this:

public string TextData
{
    get { return _textData; }
    set
    {
        _textData = value;
        OnPropertyChanged();
    }
}
C#

The compiler will fill the parameter with "TextData", with no need that we explicitly fill it. This is excellent when we change the name of a property: if we had to pass the name for the OnPropertyChanged method and we forgot to change the parameter name, we would not have the change of the UI. Using the CallerMemberName attribute, this doesn't happen.

Create a new property TextData, like we did above. Now, let's create tests for this class. Go back to the test project and create a new class, calling it MainViewModelTests.cs. There, create a new test:

[TestClass]
public class MainViewModelTests
{
    [TestMethod]
    public void CreateMainViewModel_TextDataShouldHaveText()
    {
        var viewModel = new MainViewModel();
        Assert.AreEqual(
          "The current date is 01/01/2013 (Tuesday)", viewModel.TextData);
    }
}
C#

When we execute this test, it doesn't pass, because we didn't create any code for assigning TextData.

We must create the code for the test to pass. Create the constructor of the MainViewModel class:

public MainViewModel()
{
    _textData = string.Format("The current date is {0:d} ({0:dddd})", 
       new DateTime(2013,1,1));
}
C#

The test passes. We now refactor the code, creating a GetTextDate method:

public MainViewModel()
{
    _textData = GetTextDate();
}

private static string GetTextDate()
{
    return string.Format("The current date is {0:d} ({0:dddd})", 
      new DateTime(2013,1,1));
}
C#

Our method is only valid for 1/1/2013. We must create another test for the other dates:

[TestMethod]
public void CreateMainViewModel_Date01022013_TextDataShouldHaveText()
{
    var viewModel = new MainViewModel();
    Assert.AreEqual("The current date is 01/02/2013 (Wednesday)", 
      viewModel.TextData);
}
C#

This new test doesn't pass. We need to go deeper - we need to change our program to retrieve the current date, but our test won't work anymore (and won't be reliable, as the date changes every day J), unless we use Fakes again. As we said before, this is not recommended. We need to remove the dependency of DateTime.

According to the book "Beautiful code", "all problems in computer science can be solved by another level of indirection". That's what we will do, create a level of indirection, an interface IDateTimeProvider, which will provide our dates. Create a folder and name it Interfaces and create a new interface IDateTimeProvider in it:

public interface IDateTimeProvider
{
    DateTime Now { get; } 
}
C#

Implement this interface in the class SystemDateTimeProvider:

public class SystemDateTimeProvider : IDateTimeProvider
{
    public DateTime Now
    {
        get { return DateTime.Now; }
    }
}
C#

We will pass this interface to the ViewModel using the Constructor Injection technique: we pass the interface to the constructor of the class and the interface is responsible for providing the current date. When we want the real data, we use the SystemDateTimeProvider class and when we want to test the code, we use a fake class created only for that.

The constructor of MainViewModel is changed to receive the new interface:

private readonly IDateTimeProvider _dateTimeProvider;

public MainViewModel(IDateTimeProvider dateTimeProvider)
{
    _dateTimeProvider = dateTimeProvider;
    _textData = GetTextDate();
}
C#

With this change, we can alter the code of GetTextDate to use the provider:

private string GetTextDate()
{
    return string.Format("The current date is {0:d} ({0:dddd})", 
      _dateTimeProvider.Now);
}
C#

Our tests need to be changed to pass the provider to the constructor of MainViewModel. We need to create a class that implements the interface and does what we want regarding to the date. In MainViewModelTestscreate the class FakeDateTimeProvider:

public class FakeDateTimeProvider : IDateTimeProvider
{
    private readonly DateTime _currentDateTime;

    public FakeDateTimeProvider(DateTime currentDateTime)
    {
        _currentDateTime = currentDateTime;
    }

    public DateTime Now
    {
        get { return _currentDateTime; }
    }
}
C#

This class receives in the constructor the date we want as the current date. Now we can change our tests, passing an instance of this class:

[TestMethod]
public void CreateMainViewModel_TextDataShouldHaveText()
{
    IDateTimeProvider provider = new FakeDateTimeProvider(
       new DateTime(2013,1,1));
    var viewModel = new MainViewModel(provider);
    Assert.AreEqual("The current date is 01/01/2013 (Tuesday)", 
       viewModel.TextData);
}

[TestMethod]
public void CreateMainViewModel_Date01022013_TextDataShouldHaveText()
{
    IDateTimeProvider provider = new FakeDateTimeProvider(
       new DateTime(2013, 1, 2));
    var viewModel = new MainViewModel(provider);
    Assert.AreEqual("The current date is 01/02/2013 (Wednesday)",
       viewModel.TextData);
}
C#

Our tests pass with no problems and we have removed the dependency to DateTime. The next step is to bind the ViewModel to the View. This is done setting the DataContext property of the view to an instance of the viewmodel. One way to do that is to put the following code on the constructor of the view:

public MainWindow()
{
    InitializeComponent();
    IDateTimeProvider provider = new SystemDateTimeProvider();
    DataContext = new MainViewModel(provider);
}
C#

This works fine, but it has two problems:

  • We have to create an instance of SystemDateTimeProvider to pass it to the constructor of the viewmodel.
  • The viewmodel doesn't work at design time, thus making it difficult to design the UI.

To overcome these two problems, we will use the ViewModelLocator technique, a class that will use Dependency Injection to locate and instantiate our viewmodels. We will use a Dependency injection framework to do it. There are many good ones in the market, most of them free: Castle Windsor, Autofac, StructureMap and Ninject are very good, but we will use Unity, a framework created by Microsoft Patterns and Practices.

We can install it using Nuget. On the solution Explorer, right-click the References node and choose "Manage Nuget Packages". Search for Unity and install it:

Unity works in the following way: we register the interfaces with the associated concrete classes in the container and then we call the Resolve method to return the classes instances - all dependencies are resolved and the class instance is returned. There is no need to register the concrete classes if Unity can create them (by having a default parameterless constructor or with dependencies that can be resolved).

For example, the register phase in our classes would be:

_container = new UnityContainer();
_container.RegisterType<IDateTimeProvider, SystemDateTimeProvider>();
C#

To get an instance of MainViewModel, we use something like:

_container.Resolve<MainViewModel>();
C#

We do not need to register the MainViewModel class, because Unity can resolve all dependencies with the registered interfaces. Now, we can create a new class on the folder ViewModel, named ViewModelLocator:

private readonly UnityContainer _container;

public ViewModelLocator()
{
    _container = new UnityContainer();
    _container.RegisterType<IDateTimeProvider, SystemDateTimeProvider>();
}

public MainViewModel Main
{
    get { return _container.Resolve<MainViewModel>(); }
}
C#

We register theIDateTimeProvider interface, associating it to the SystemDateTimeProvider class and create a property named Main that returns an instance of MainViewModel. To use this class, we add an instance to the Resources section of App.xaml. In App.xaml, add this code:

<Application.Resources>
     <viewModel:ViewModelLocator x:Key="Locator"/>
</Application.Resources>
XML

Add the namespace for the ViewModelLocator in the Application class declaration:

<Application 
    x:Class="FakesTest.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:viewModel="clr-namespace:FakesTest.ViewModel"
    StartupUri="MainWindow.xaml">
XML

Finally, in MainWindow, set the property DataContext to the property Mainof the ViewModelLocator and bind the property Text of the TextBlock to the property TextData of the ViewModel:

<Window x:Class="FakesTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        DataContext="{Binding Source={StaticResource Locator}, Path=Main}">
    <Grid>
        <TextBlock x:Name="TxtDate" Text="{Binding TextData}"/>
    </Grid>
</Window>
XML

When we do that, the current date data appears at design time:

We can then remove the code behind and our code in MainWindow.xaml.cs stays like that:

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
    }
}

Execute the program and observe that it behaves exactly in the same way as it did before. When we execute our tests, we see that they pass, but the first test (the one we've created in the prior article) doesn't pass:

Test method FakesTest.Tests.MainWindowTests.CreateWindow_TextBlockHasCurrentDate threw exception: 
System.Windows.Markup.XamlParseException: 'Provide value on 'System.Windows.StaticResourceExtension' threw an exception.' Line number '5' and line position '9'. ---> System.Exception: Cannot find resource named 'Locator'. Resource names are case sensitive.

This is due to the fact that we have the dependency between the View and the ViewModelLocator. As we wanted to remove the dependency of Fakes, we can ignore this test, adding the Ignore attribute (we could remove this test, as it doesn't make sense anymore, but I decided to leave it, as a reminder of the old times that should never come back J):

[Ignore]
[TestMethod]
public void CreateWindow_TextBlockHasCurrentDate()
{
    using (ShimsContext.Create())
    {
        ShimDateTime.NowGet = () => new DateTime(2013,1,1);
        var window = new MainWindow();
        var mainGrid = (Grid)window.Content;
        var textBlock = (TextBlock) mainGrid.Children[0];
        Assert.AreEqual("The current date is 01/01/2013 (Tuesday)",
           textBlock.Text);
    }
}

Conclusions

We finished our restructuring. Our code, once dependent of the Window and to DateTime, making it almost impossible to test, is now detached and 100% testable.

For that, we restructured the code using a ViewModel and Dependency Injection that resolves our dependencies in an automatic way, giving us a bonus of having the data at design time. We don't need to use Fakes anymore and the program is ready for new updates.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

  • May 2025
  • December 2024
  • October 2024
  • August 2024
  • July 2024
  • June 2024
  • November 2023
  • October 2023
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • November 2022
  • October 2022
  • September 2022
  • August 2022
  • June 2022
  • April 2022
  • March 2022
  • February 2022
  • January 2022
  • July 2021
  • June 2021
  • May 2021
  • April 2021
  • March 2021
  • February 2021
  • January 2021
  • December 2020
  • October 2020
  • September 2020
  • April 2020
  • March 2020
  • January 2020
  • November 2019
  • September 2019
  • August 2019
  • July 2019
  • June 2019
  • April 2019
  • March 2019
  • February 2019
  • January 2019
  • December 2018
  • November 2018
  • October 2018
  • September 2018
  • August 2018
  • July 2018
  • June 2018
  • May 2018
  • November 2017
  • October 2017
  • September 2017
  • August 2017
  • June 2017
  • May 2017
  • March 2017
  • February 2017
  • January 2017
  • December 2016
  • November 2016
  • October 2016
  • September 2016
  • August 2016
  • July 2016
  • June 2016
  • May 2016
  • April 2016
  • March 2016
  • February 2016
  • October 2015
  • August 2013
  • May 2013
  • February 2012
  • January 2012
  • April 2011
  • March 2011
  • December 2010
  • November 2009
  • June 2009
  • April 2009
  • March 2009
  • February 2009
  • January 2009
  • December 2008
  • November 2008
  • October 2008
  • July 2008
  • March 2008
  • February 2008
  • January 2008
  • December 2007
  • November 2007
  • October 2007
  • September 2007
  • August 2007
  • July 2007
  • Development
  • English
  • Português
  • Uncategorized
  • Windows

.NET AI Algorithms asp.NET Backup C# Debugging Delphi Dependency Injection Desktop Bridge Desktop icons Entity Framework JSON Linq Mef Minimal API MVVM NTFS Open Source OpenXML OzCode PowerShell Sensors Silverlight Source Code Generators sql server Surface Dial Testing Tools TypeScript UI Unit Testing UWP Visual Studio VS Code WCF WebView2 WinAppSDK Windows Windows 10 Windows Forms Windows Phone WPF XAML Zip

  • Entries RSS
  • Comments RSS
©2025 Bruno Sonnino | Design: Newspaperly WordPress Theme
Menu
  • Home
  • About