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
- Visual studio 2010 SP1 or 2012 (you can get VS 2010 SP1 here)
- Silverlight 4 Toolkit - April 2010 (download here)
- ...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.
However, if you want to create yourself a solution structure you can create a new Silverlight project as shown in the screenshot below.
Projects and the connections
This application offers the functions
- Display employees
- Create new employee
- 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)
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
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.
[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.
Here is the MethodResult
[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.
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:
Here is the implementation of service methods (except Getemployees
method other methods are taken out)
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
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.
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)
public abstract class EventManager
{
private readonly EventSet _eventSet = new EventSet();
protected EventSet EventSet { get { return _eventSet; } }
}
public sealed class EventSet
{
private readonly Dictionary<EventKey, Delegate> _mEvents =
new Dictionary<EventKey, Delegate>();
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);
}
public void Remove(EventKey eventKey, Delegate handler)
{
Monitor.Enter(_mEvents);
Delegate d;
if (_mEvents.TryGetValue(eventKey, out d))
{
d = Delegate.Remove(d, handler);
if (d != null) _mEvents[eventKey] = d;
else _mEvents.Remove(eventKey);
}
Monitor.Exit(_mEvents);
}
public void Raise(EventKey eventKey, params object[] values)
{
Delegate d;
Monitor.Enter(_mEvents);
_mEvents.TryGetValue(eventKey, out d);
Monitor.Exit(_mEvents);
if (d != null)
{
d.DynamicInvoke(values);
}
}
}
An event is created using this EventManager.
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
Once the call is completed
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
public partial class Employees
{
public Employees()
{
InitializeComponent();
}
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.
<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
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