Click here to Skip to main content
15,884,298 members
Articles / Programming Languages / C# 4.0

Silverlight with MVVM and WCF

Rate me:
Please Sign up or sign in to vote.
4.69/5 (8 votes)
23 Jul 2013CPOL7 min read 29.1K   1.1K   10   5
This article is aimed at helping develop a business application using Silverlight with MVVM and WCF service in a more manageable way.

Introduction

The purpose of this article is to explain one of many ways to develop a business application using Silverlight, MVVM, and WCF. I will try explaining the different components required and their relationships to achieve this by developing a small Silverlight Navigation application for a fictitious company called XYZ.

Pre-Requisite 

To open and run the source code you need

  1. Visual studio 2010 SP1 or 2012 (you can get VS 2010 SP1 here)
  2. Silverlight 4 Toolkit - April 2010 (download here)
  3. ...and basic knowledge of Silverlight, MVVM and WCF.Why don't you learn them now?

Solution Structure

 This is a Silverlight Navigation application project and the Solution explorer has 12 projects organized under different solution folders for easy understanding and maintainable.

Image 1

However, if you want to create yourself a solution structure you can create a new Silverlight project as shown in the screenshot below.

Image 2

Projects and the connections 

This application offers the functions

  1. Display employees
  2. Create new employee
  3. Update Employee

Ah! very simple, but why it is required to have 12 projects in the solution? The answer is separation of concerns and testable. The front end is not a priority (you can easily add more pages and complex design using this structure, later) but the architecture is.

Here is the dependency diagram created using the Architecture  menu from VS 2010 (Note: this menu available only in ultimate edition)

Image 3

So, the web hosts the Silverlight project (XYZ) which renders screens and logic using MVVM pattern and WCF service. Let us get into little detail on these topics.

MVVM Brief

This article's intent is not to teach MVVM in detail (and you know how to get details), but in summary it breaks the links between the user interface and the business. MVVM is best suited for Silverlight and WPF because of its rich binding support

MVVM stands for Model View ViewModel and the interactions between them is

Image 4

Model - holds all business related stuffs. In our case XYZ.Model uses the WCF service to get, add, and update employee.

View - takes care of all screens and all user pleasing stuffs. In our case it is the XYZ project.

View Model - a single contact point for view. view does not need to know a model exists but ask view model for anything it requires. It is the XYZ.ViewModel project in our solution.

Deep dive into code

Service Contract

The service part contains three projects: XYZ.Service,XYZ.Data and XYZ.Contract

The service contracts and data are separated from the service implementation for easy maintenance and testing. you can write test methods using fake implementation.

Here is the service interface.

C#
[ServiceContract(Namespace = "http://XYZ")]
public interface IEmployeeService
{
    [OperationContract]
    MethodResult<List<Employee>> GetEmployees();
    [OperationContract]
    MethodResult<Employee> GetEmployee(int employeeId);
    [OperationContract]
    MethodResult<int> SaveEmployee(Employee employee);
    [OperationContract]
    MethodResult<int> DeleteEmployee(int? employeeId);
}

The MethodResult<t> is a generic class and all the service method should return this type by passing the return value to the generic type. For example the method GetEmployees returns list of employee. This approach has some benefits too.

  • Return type of each method is always MethodResult<t /> and it can be set as a standard for the development team to follow.(I don't know how many of you asked this yourself : How do I create a method which does not need to return any value back to the caller? Great, the answer is... I do not know. Can someone give answer, please?)
  • Exception not thrown to the client but wrapped by other properties. Please note that any exception thrown by the service gets translated by the browser into a 404 File Not Found error and you always get:

    Image 5

    But in this approach, we can show the user the correct error message like

    Image 6

Here is the MethodResult

C#
[DataContract]
public class MethodResult<T>
{
    [DataMember]
    public Status Status { get; set; }

    [DataMember]
    public string ErrorMessage { get; set; }

    [DataMember]
    public T Data { get; set; }

    public MethodResult()
    {
        Status = Status.Unknown;
    }
}

Service Implementation

IEmployeeService is implemented by EmployeeService class which uses a repository pattern for data store. The repository XYZ.Repository project wraps the underlying storage system, it could be a LINQ to SQL or Entity framework or any other. Here the data is stored in memory.
C#
public class EmployeeService : IEmployeeService
{
    private readonly ILogService _logService;
    private readonly IEmployeeRepository _repository;


    public EmployeeService(IEmployeeRepository repository)
    {
        _repository = repository;
        _logService = new FileLogService(typeof(EmployeeService));
    }

    public EmployeeService()
        : this(new EmployeeRepository())
    {

    }
}

As you notice here the constructor takes the repository interface making easier to write test methods using fake classes. you can see a sample test methods in the project XYZ.Service.Test.Also, you see the ILogService interface initialized in the constructor which helps logging. This XYZ.Log project is using log4net v1.2.11 to write information to log file.This project is taken from the article I have written a year back in code project titled log4Net logging framework

A sample log file:

Image 7

Here is the implementation of service methods (except Getemployees method other methods are taken out)

C#
public MethodResult<List<Employee>> GetEmployees()
{
    Enter("GetEmployees");
    var result = new MethodResult<List<Employee>>();

    try
    {
        result.Data = _repository.GetEmployees();
        result.Status = Status.Success;
    }
    catch (Exception ex)
    {
        result.ErrorMessage = ex.Message;
        result.Status = Status.Error;
        Error(ex);
    }

    Exit();
    return result;
}

Front end

XYZ.Model

The WCF service is referenced in the XYZ.Model project. It has its own interface to enable the view model to interact with.Again, this interface approach will help us writing test methods

C#
public interface IEmployeeService
{
    event Action<string> GetEmployeesError;
    event Action<string> GetEmployeeError;
    event Action<string> SaveEmployeeError;

    event Action<ObservableCollection<Employee>> GetEmployeesCompleted;
    event Action<Employee> GetEmployeeCompleted;
    event Action<int> SaveEmployeeCompleted;

    void GetEmployees();
    void GetEmployee(int employeeId);
    void SaveEmployee(Employee employee);
}

As you see this interface offers events double to the number of methods. That is six events for three methods (each for completion and failure). This events are here to communicate the status of a method to the view model which in turn inform the view.

XYZ.ViewModel

Viewmodel helps view to get data to manipulate. all view model will inherit from the ViewModelBase class. This class has implemented the INotifyPropertyChanged interface to help notify the view when something changed in the viewmodel. This helps the view to update our viewmodel data directly using bindings in the XAML or code-behind code.

C#
public abstract class ViewModelBase : EventManager, INotifyPropertyChanged
{

    protected void RaisePropertyChanged(string property)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(property));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public virtual bool IsValid()
    {
        return true;
    }
}

As you notice it also inherits from a class EventManager. When you define an event in a class, the compiler creates add and remove accessor for each event so the code size increases, but not all the events might be used by the caller at time. The extra size can be reduced if we create accessors for only the accessed events.

EventManager class exposes a EventSet property which manages to create those accessors only when someone shows interest to the event.For example, a class exposes 10 events but only two events are used by the caller then only these two event accessors are created by the compiler thus reducing the code size. This concept is not mine but the author of CLR via C#, Jeffrey Richter. In fact the class EventSet is taken from that book only with a slight modification in the method Raise (see the chapter 11:Events)

C#
public abstract class EventManager
{
    private readonly EventSet _eventSet = new EventSet();

    protected EventSet EventSet { get { return _eventSet; } }
}

public sealed class EventSet
{

    /// <summary>
    /// The private dictionary used to maintain EventKey -> Delegate mappings
    /// </summary>
    private readonly Dictionary<EventKey, Delegate> _mEvents = 
            new Dictionary<EventKey, Delegate>();
    
    /// <summary>
    /// Adds an EventKey -> Delegate mapping if it doesn't exist or
    /// combines a delegate to an existing EventKey
    /// </summary>
    /// <param name="eventKey"></param>
    /// <param name="handler"></param>
    public void Add(EventKey eventKey, Delegate handler)
    {
        Monitor.Enter(_mEvents);
        Delegate d;
        _mEvents.TryGetValue(eventKey, out d);
        _mEvents[eventKey] = Delegate.Combine(d, handler);
        Monitor.Exit(_mEvents);
    }
        
    /// <summary>
    /// Removes a delegate from an EventKey (if it exists) and
    /// removes the EventKey -> Delegate mapping the last delegate is removed
    /// </summary>
    /// <param name="eventKey"></param>
    /// <param name="handler"></param>
    public void Remove(EventKey eventKey, Delegate handler)
    {
        Monitor.Enter(_mEvents);
        // Call TryGetValue to ensure that an exception is not thrown if
        // attempting to remove a delegate from an EventKey not in the set
        Delegate d;
        if (_mEvents.TryGetValue(eventKey, out d))
        {
            d = Delegate.Remove(d, handler);
            // If a delegate remains, set the new head else remove the EventKey
            if (d != null) _mEvents[eventKey] = d;
            else _mEvents.Remove(eventKey);
        }
        Monitor.Exit(_mEvents);
    }
        
    /// <summary>
    ///  Raises the event for the indicated EventKey 
    /// </summary>
    /// <param name="eventKey"></param>
    /// <param name="values"> </param>
    public void Raise(EventKey eventKey, params object[] values)
    {
        // Don't throw an exception if the EventKey is not in the set
        Delegate d;
        Monitor.Enter(_mEvents);
        _mEvents.TryGetValue(eventKey, out d);
        Monitor.Exit(_mEvents);
        if (d != null)
        {
            // Because the dictionary can contain several different delegate types,
            // it is impossible to construct a type-safe call to the delegate at
            // compile time. So, I call the System.Delegate type's DynamicInvoke
            // method, passing it the callback method's parameters as an array of

            // objects. Internally, DynamicInvoke will check the type safety of the
            // parameters with the callback method being called and call the method.
            // If there is a type mismatch, then DynamicInvoke will throw an exception.
            d.DynamicInvoke(values);
        }
    }
}

An event is created using this EventManager.

C#
static readonly EventKey LoadEmployeesSuccessEventKey = new EventKey();
 
public event Action<ObservableCollection<EmployeeDto>> LoadEmployeesSuccess
{
    add { EventSet.Add(LoadEmployeesSuccessEventKey, value); }
    remove { EventSet.Remove(LoadEmployeesSuccessEventKey, value); }
}

void OnLoadEmployeesSuccess(ObservableCollection<EmployeeDto> employees)
{
    EventSet.Raise(LoadEmployeesSuccessEventKey, employees);
}

Since you cannot make synchronous call to WCF service from Silverlight we need some way of informing the progress to the user and events are the choice. Here is the screen with progress message while the code calls service method

Image 8

Once the call is completed

Image 9

XYZ.ViewModel is calling Model for data and it has methods and events for the view to work with. By convention there is a view model for each view and it is named [view]ViewModel.For example a EmployeesViewModel is for the view Employees. But a view model can be used by any number of views as long as they are relevant.

XYZ

Finally, the view part. The view will use the view model for wiring up events and binding data to the controls. Here is what happens when the Employees.xaml file is accessed

C#
public partial class Employees
{
    public Employees()
    {
        InitializeComponent();
    }

    // Executes when the user navigates to this page.
    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        var vm = new EmployeesViewModel();
        vm.LoadEmployeesStarted += AppHelper.ShowBusy;

        vm.LoadEmployeesSuccess += employees =>
                                       {
                                           AppHelper.StopBusy();
                                           LayoutRoot.DataContext = vm;
                                           var view = new PagedCollectionView(employees);
                                           DataGridEmployees.ItemsSource = view;
                                       };

        vm.LoadEmployeesFailed += reason =>
                                      {
                                          AppHelper.StopBusy();
                                          AppHelper.ShowMessage(string.Empty, reason);
                                      };
        vm.LoadEmployees();
    }
}

The Employee view uses the EmployeeViewModel and binds the SaveEmployeeCommand to its save button.

XML
<Button Content="Save Employee" Width="100" 
  Command="{Binding SaveEmployeeCommand}" x:Name="ButtonSave" Margin="10"/>

On clicking the button the method SaveEmployee from the EmployeeViewModel will be called

C#
void SaveEmployee()
{
    OnSaveEmployeeStarted();
    _service.SaveEmployee(CurrentEmployee.AsEmployee());
}

Finally...

It is a pleasure to see you here still, thanks! Of course, I cannot explain all the stuffs in the solution here, but I encourage you to download the code and explore yourself. If you are unclear of anything or running into any issues, please leave a comment and I will get back to you as soon as possible. Thank you again for reading thus far.

Points of Interest

This solution does not dependent on any third party controls except log4net, but you can try third party libraries for MVVM (example: MVVM light) and Testing.

History

  • Published: 23-Jul-2013.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect CGI
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Bughave bugs in your solution Pin
Member 1313462117-Apr-17 20:42
Member 1313462117-Apr-17 20:42 
GeneralRe: have bugs in your solution Pin
Prabu ram18-Apr-17 18:18
Prabu ram18-Apr-17 18:18 
QuestionThank you very much :) Pin
JustMe4TheCodeProject26-Feb-14 21:47
JustMe4TheCodeProject26-Feb-14 21:47 
Generalgood work Pin
khanzzirfan8-Feb-14 19:59
khanzzirfan8-Feb-14 19:59 
GeneralMy vote of 1 Pin
gkopparam7-Nov-13 21:13
gkopparam7-Nov-13 21:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.