Click here to Skip to main content
Click here to Skip to main content

Zombie Explorer: An n-tier application from top to bottom

, 15 Apr 2013
Rate this:
Please Sign up or sign in to vote.
A complete end to end example from DB to WPF client using MVVM/PRISM/NHibernate/Respository/IOC.

I just want the code man, give me the code, well OK chillax, it's right here:

Table of Contents

Introduction

This article is something I have been meaning to do for some time, not really for anyone's benefit other than my own really. Before we get onto what the article will cover, let me just outline why I wanted to write this article.

I have been working with both WCF/WPF a while now (I know some people consider these old technologies now, but I can assure you they are both alive and well, at least in my part of the world), and I have seen a variety of different approaches to developing an n-tier application with these technologies.

I have seen good and bad things, some of the bad things being:

  • Ridiculous amount of methods on a service, which become quite unmaintainable quite quickly
  • Everything is its own service, where you have about 30 WCF services all trying to call each other; nightmare man
  • Bad separation of concerns, where the dependency graph is all screwed up, and your UI inadvertently ends up knowing way too much as it is forced to reference things it has no business knowing about
  • Use of Reference.cs (more on this later)
  • Lack of support/thought for DD/IOC/Mocking or any of the cool stuff people take for granted nowadays

I thought it would be nice to try and see if I could make a demo app which kind of did things the way I had it mapped out in my mind. I just wanted to be able to do a complete application from database to WPF client all the way through, with only my own thoughts (and headaches/nightmares) to deal with.

I freely admit I have used certain paradigms I have seen along the way, such as issuing a Request and getting a Response. I actually like this pattern as it keeps your WCF service contract very simple, as it is basically Response ExecuteRequest(Request req), which means no matter how much stuff you add, your service contract stays up to date, that is providing you keep your WCF KnownType(s) up to date.

For the attached demo code I wanted to make sure I covered the following aspects:

  • Provide good level of separation of concerns, that is the UI should not care about server side business logic, why should it?
  • Be able to easily easily swap out/test certain areas of our application (IOC allows this)
  • Database access should be as lean as possible (NHibernate and Repository pattern usage facilitate this)
  • Should be able to test a small part of the overall system

I have called the project "WcfExemplar" which I hope people do not find arrogant, it is certainly not meant to be. It is called that more for me really, as I was doing this to prove something to myself, so it was an "Exemplar" for me if you will.  I hope you forgive me this one indulgence.

So what does the demo app do?

It is a WPF (Prism/MVVM based) client which communicates with a Console hosted WCF service application. The application allows users to search database stored zombie incidents. The user may also add new zombie incidents. So it's actually not that complicated, it is basic CRUD stuff.

I think the main salient points are as follows:

  • WPF front end (that I will not dwell on too much, as that is a mere vehicle to illustrate the server side concepts) which allows users to
    • Search for items (I am storing zombie data where a heading/description and GeoLocation data are stored for each zombie incident)
    • Add new items
    • View items on a map (I have chosen to use Bing maps)
    • Use Rx to show an RSS feed of global zombie incidents (as a bonus if you will)
  • It uses a shared DLL where only the relevant things are shared between client and server, which ensures the dependency graph make sense. So things like those shown below would typically be shared
    • Business Objects
    • Service contract
    • Fault contracts
  • There is no auto generated Reference.xx or client proxy, we use a shared DLL and hand craft our own client proxy
  • It uses Fluent NHibernate for its persistence, where ISession provides the Unit Of Work (UOW)
  • It uses IOC such that any component that talks to an external part (i.e., database) can be easily substituted. This is done from the WCF service down
  • It uses business logic to do any work on objects before any attempt to persist an object is done.
  • It keeps connections to the database at a minimum and only engages the database for the minimum amount of time.

Please try and remember I am writing this using a Request/Response mechanism, so even if you don't like the overall structure of the Request/Response/Task approach, I hope that there will be some bits and pieces in here to keep you interested.

Pre-Requisites

In order to run the attached code you will need to have the following components

  1. A reasonably good PC (One that is capable of running WPF and SQL Server)
  2. Your own SQL Server installation
  3. This demo uses Bing Maps, as such you will need to acquire a Bing Maps developer API key (or just put up with the nag water mark message that the demo will show if you do not have an API key, which to be honest is not that big a deal, you call). You can obtain a Bing maps API key: http://msdn.microsoft.com/en-us/library/ff428642.aspx
  4.  Steady connection to the internet is required, as the UI assumes it is web connected in order to display maps and read RSS feeds

Getting Started

This mini step by step guide will tell you what you need to do in order to get the demo app up and running

  1. Create a new database in SQL server called "WcfExemplar"
  2. Run the following SQL scripts
    • 02 Create ZombieIncidents table.sql
    • 03 Create GeoLocations table.sql
    • 04 Create some dummy data.sql
  3. Change the "ZombieDB" SQL connection string in the App.Config within the "Hosting\ConsoleHost\WcfExamplar.Host" project to point to
    your own SQL Server installation
  4. Sign up for a Bing maps API key (if you want to get rid of the nag message), and enter your details in the XAML of these files
    • WcfExemplar.WpfClient.Common\Controls\SlimLineZombieIncidentMapView.xaml, look for the Bing map control and fill in the line "CredentialsProvider="INSERT_YOUR_BING_MAPS_KEY""
    • WcfExemplar.WpfClient.MapModule\ZombieIncidentMapView.xaml, look for the Bing map control and fill in the line "CredentialsProvider="INSERT_YOUR_BING_MAPS_KEY""
  5. Run the WCF console host by right clicking the "Hosting\ConsoleHost\WcfExamplar.Host" project, and Debug->Start New Instance
  6. Run the WPF client by right clicking the "UI\PRISM\WcfExemplar.WpfClient" project, and Debug->Start New Instance

The Server Side: WCF

This section will talk about the WCF service layer. In a nutshell what the WCF service does is provide a single method interface service contract. The client will call the WCF service using Requests, which are available in a shared DLL.

Requests immediately provide their properties to a Task which in turn will call into the appropriate business logic. If the business logic checks that a certain action is sound, and passes whatever specific validation is needed, communication with the persistence layer is permitted. Where I am using Fluent NHibernate UOW and Repository patterns. 

The Need For Layers - Why Bother

By now you should appreciate that there is a Service that accepts Request(s) which allow Task(s) to be created, where the Task(s) are the real guys doing the work.

This article code had something like this

Request(s) get exposed in shared DLL. The looking up of a Task(s) is done via an within the actual WCF service using a tiny bit of reflection which is only ever done within the WCF service, so the WCF clients no nothing about this or any of Task(s) existing dependencies such as x/y and z. So you only ever get the stuff that should be shared.

The reason we should care about this comes down to "separation of concerns". The WCF client should not care about anything other than the need to get/send business objects, and use Request/Response objects via a common service contract. Ok there will also be shared FaultContract objects but these are also part of the shared DLL between the WCF service and the WCF client. 

To my mind this is a very important piece of the jigsaw solved, this provides a good separation of concerns, and also allows reuse of critical pieces of shared code.

The WCF Service

As previously stated the WCF is pretty simple, here is the shared service contract interface, see how it literally has one method, which takes a Request and returns a Response.

[ServiceContract]
public interface IGateway
{
    [OperationContract]
    [FaultContract(typeof(GenericFault))]
    [FaultContract(typeof(BusinessLogicFault))]
    Response ExecuteRequest(Request request);
}

Where this service contract is shared with the client via a shared DLL. The full service implementation looks like this

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class Gateway : IGateway
{
    public Response ExecuteRequest(Request request)
    {
        WcfExemplar.Common.Logging.LogManager.Instance.Logger("Gateway")
            .InfoFormat("Executing request : {0}", request.GetType().Name);

        object requestHandler = null;
        try
        {
            Type requestType = request.GetType();
            Type responseType = GetResponseType(requestType);
            Type requestHandlerType = typeof(IRequestHandler<,>).MakeGenericType(requestType, responseType);
            requestHandler = container.Resolve(requestHandlerType);
            return (Response)requestHandlerType.GetMethod("Handle").Invoke(requestHandler, new[] { request });
        }
        catch (BusinessLogicException bex)
        {
            BusinessLogicFault bf = new BusinessLogicFault();
            bf.Operation = requestHandler.GetType().FullName;
            bf.Message = bex.Message;
            throw new FaultException<BusinessLogicFault>(bf);
        }
        catch (Exception ex)
        {
            GenericFault gf = new GenericFault();
            gf.Operation = requestHandler.GetType().FullName;
            gf.Message = ex.Message;
            throw new FaultException<GenericFault>(gf);
        }
    }

    private static Type GetResponseType(Type requestType)
    {
        return GetRequestInterface(requestType).GetGenericArguments()[0];
    }

    private static Type GetRequestInterface(Type requestType)
    {
        return (
            from @interface in requestType.GetInterfaces()
            where @interface.IsGenericType
            where typeof(IRequest<>).IsAssignableFrom(
                @interface.GetGenericTypeDefinition())
            select @interface)
            .Single();
    }
}

There are a couple of things to note here, which are as follows:

  • We can see we are using FaultExceptions to send faults to the WCF client (the ones we allow are attributed on the shared service contract)
  • We use a small bit of reflection to obtain a IOC container registered instance of IRequestHandler<,> which just happens to be the Task that is able to take an instance of the current Request (which is how we provide a nice separation of concerns, and are able to share only what we should)
  • We use the Castle Windsor IOC container (more on this later)
  • We use InstanceModeContext.PerCall, as this is what seems to be best practice when working with NHibernate and its ISession UOW object. ISession should be short and snappy and should not be long living at all

This service is hosted in a simple Console application host (see WcfExamplar.Host project if you are interested in that)

IOC

"Inversion Of Control" is in my opinion, an absolute god send that aids the development of enterprise level applications.

It basically facilitates a more decoupled architecture, and also encourages the use of interfaces (strategy pattern if you ask me) which in turn creates a better avenue for testing. With these couple of points in mind it was my intention to provide IOC support from the WCF service down. Such that it can be used at every level in the server side WCF code.

So what IOC library do I use? Well I have a couple of favourites, but Castle Windsor is right up there, so I have made use of that.

The WCF service itself does very little work, except to find a Task and run it. So it is Tasks where all the real work starts to get done, so it should come as no surprise that the WCF service doesn't have any IOC registered dependencies. It does however setup the Castle Windsor  IOC Container on aper call, yes I said PerCall instance context.

Just to The reason I use PerCall is that I am using NHibernate. The ISession UnitOfWork that NHibernate uses, is meant for short snappy usage, and should not be kept alive for long periods of time. It is most commonly used with web sites actually, which all operate using a per request arrangement, which in my mind is most analogous to a PerCall WCF instance context service.

This is done as follows:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class Gateway : IGateway
{
    
}

Castle Windsor allows us to configure the IOC container by providing one or more container installer files. I am using a single Castle Windsor container installer which looks like this:

public class StandardInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        string dbConnectionString = ConfigurationManager.ConnectionStrings["ZombieDB"].ConnectionString;

        container.AddFacility<WcfFacility>()
        .Register(
            Component.For<NHibernateHelper>().DependsOn(new
                    {
                        connectionString = dbConnectionString
                    }).LifeStyle.Singleton,
            Component.For<Func<ISessionFactory>>().Instance(() => 
               container.Resolve<NHibernateHelper>().SessionFactory).LifeStyle.Singleton,
            Component.For<Func<ISession>>().Instance(() => 
              ((INHibSessionProvider)container.Resolve<IUnitOfWork>()).Session).LifeStyle.PerWcfOperation(),
            Component.For<IUnitOfWork>().ImplementedBy<NHibernateUnitOfWork>().LifeStyle.PerWcfOperation(),
            Component.For<IRepository<ZombieIncident>>().ImplementedBy<NHibernateRepository<ZombieIncident>>().LifeStyle.PerWcfOperation(),
            Component.For<IRepository<GeoLocation>>().ImplementedBy<NHibernateRepository<GeoLocation>>().LifeStyle.PerWcfOperation(),
            Component.For<IZombieIncidentDomainLogic>().ImplementedBy<ZombieIncidentDomainLogic>().LifeStyle.Transient,
            Component.For<IGateway>().ImplementedBy<Gateway>().LifeStyle.PerWcfOperation()
        );
    }
}

It can be seen above that this uses a couple of neat castle features such as anonymous objects and the use of Func<T> delegates, which are used as lightweight factories to provide Fluent NHibernate related classes with values. We will see more on this later.

Another point to note is that I am using the Castle WCF facility and also make use of the Castle Service Host (which the most important parts of are shown below)

IOCManager.Container.Install(new IWindsorInstaller[] { new StandardInstaller(), new TaskInstaller() });
simpleServiceHost = new DefaultServiceHostFactory(IOCManager.Container.Kernel)
    .CreateServiceHost(typeof(IGateway).AssemblyQualifiedName
    , new Uri[0]);
StartServiceHost(simpleServiceHost);

We also make use of another installer that will register all Tasks within the Castle Windsor IOC container. This is shown below

public class TaskInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(AllTypes
            .FromAssemblyContaining(typeof(SaveZombieIncidentTask))
            .BasedOn(typeof(IRequestHandler<,>))
            .Unless(t => t.IsGenericTypeDefinition)
            .WithService.Select((_, baseTypes) =>
            {
                return
                    from t in baseTypes
                    where t.IsGenericType
                    let td = t.GetGenericTypeDefinition()
                    where td == typeof(IRequestHandler<,>)
                    select t;
            }).LifestyleTransient());
        }
}

This installer basically examines all types in the WcfExamplar.Server.Tasks.Dll and looks for any type that implements the IRequestHandler<,> interface. This interface should only ever be implemented by Tasks

Requests

Requests are what the WPF client (or any other client of the WCF service) would use to talk to the WCF service. As such a Request based object is part of the shared contracts, that can be found in the shared DLL between the WPF client and the WCF service. Any specific Request is nothing more that a bunch of values that capture inputs that should be used to hand to a Task.

So Request(s) can be thought of as property bags that immediately absolve themselves of any responsibility by finding an associated Task and telling the Task, hey here is what the UI asked you to do, go do it. The important part here is, that the WPF client knows nothing about Tasks(s), because that is bad, if it did it would be forced to reference things like NHibernate.dll, does that sound like a UI concern to you. No me neither.

Shown below is a typical Request

[DataContract]
public class SaveZombieIncidentRequest : Request, IRequest<SaveZombieIncidentResponse>
{
    [DataMember]
    public ZombieIncidentInfo ZombieIncidentInfo { get; set; }

    public SaveZombieIncidentRequest(ZombieIncidentInfo zombieIncidentInfo)
    {
        ZombieIncidentInfo = zombieIncidentInfo;
    }
}

There are only two considerations when using Request(s), which are

  • Making sure that the Request inherits from IRequest<T> where T is the expected Response type. This is used by the WCF service to do some metadata lookups.
  • Making sure that we attribute up Request to ensure that it knows how to serialize its sub classes, this is done as follows for the demo app.
[DataContract]
[KnownType(typeof(SaveZombieIncidentRequest))]
[KnownType(typeof(ZombieIncidentsRequest))]
[KnownType(typeof(SearchZombieIncidentsRequest))]
public abstract class Request
{
}

The same is true for Response and its sub classes.

Request to Task Mapping

As a Request ONLY really exists to be mapped to a Task, we are able to use a small but funky bit of reflection that essentially just tries to find a IRequestHandler<TRequest,TResponse> implementing class within the IOC container. The ONLY classes that should implement this interface are Task(s). As such when the relevant Task is found, we simply invoke its Handle method where the current Request is provided as a method parameter.

public Response ExecuteRequest(Request request)
{
    object requestHandler = null;
    try
    {
        Type requestType = request.GetType();
        Type responseType = GetResponseType(requestType);
        Type requestHandlerType = typeof(IRequestHandler<,>).MakeGenericType(requestType, responseType);
        requestHandler = IOCManager.Container.Resolve(requestHandlerType);
        return (Response)requestHandlerType.GetMethod("Handle").Invoke(requestHandler, new[] { request });
    }
    catch (BusinessLogicException bex)
    {
    ....
    ....
    ....
    }
    catch (Exception ex)
    {
    ....
    ....
    ....
    }
}

The small bit of reflection in the WCF service allows for a good separation of concerns between Task(s)/Request(s).  Task(s) know about Request(s), but Request(s) does not know about Task(s). Therefore Requests can be shared in a shared DLL no problem.

This is what a typical Request/Task look like, I hope this makes sense.

[DataContract]
public class SaveZombieIncidentRequest : Request, IRequest<SaveZombieIncidentResponse>
{
    [DataMember]
    public ZombieIncidentInfo ZombieIncidentInfo { get; set; }

    public SaveZombieIncidentRequest(ZombieIncidentInfo zombieIncidentInfo)
    {
        ZombieIncidentInfo = zombieIncidentInfo;
    }
}

public class SaveZombieIncidentTask : IRequestHandler<SaveZombieIncidentRequest, SaveZombieIncidentResponse>
{
    private IZombieIncidentDomainLogic zombieIncidentLogic;
    private IRepository<dtos.ZombieIncident> zombieRepository;
    private IRepository<dtos.GeoLocation> geoLocationRepository;
    private IUnitOfWork unitOfWork;
    private ZombieIncidentInfo zombieIncidentInfo;

    public SaveZombieIncidentTask(IZombieIncidentDomainLogic zombieIncidentLogic,
        IRepository<dtos.ZombieIncident> zombieRepository, 
    IRepository<dtos.GeoLocation> geoLocationRepository,
        IUnitOfWork unitOfWork)
    {
        this.zombieIncidentLogic = zombieIncidentLogic;
        this.zombieRepository = zombieRepository;
        this.geoLocationRepository = geoLocationRepository;
        this.unitOfWork = unitOfWork;
    }

    public SaveZombieIncidentResponse Handle(SaveZombieIncidentRequest request)
    {
        try
        {
            //get data
            this.zombieIncidentInfo = request.ZombieIncidentInfo;
        ....
        ....
        ....
        ....
            return new SaveZombieIncidentResponse(false);
        }
        catch (BusinessLogicException bex)
        {
            throw;
        }
        catch (Exception ex)
        {
            WcfExemplar.Common.Logging.LogManager.Instance.Logger("Tasks")
        .ErrorFormat("{0}\r\n{1}", ex.Message, ex.StackTrace);
            throw;
        }
    }
}

Tasks

Tasks(s) are kind of small units of work, but they should never call other tasks. What they are meant for really is to take some Request parameters, run the data through some business logic, and possibly hit the persistence layer using NHibernate (more on this later).

So if you like, Task(s) are simply there to facilitate running business logic and talking to the persistence layer.

Here is what a typical Task may look like

public class SaveZombieIncidentTask : IRequestHandler<SaveZombieIncidentRequest, SaveZombieIncidentResponse>
{
    private IZombieIncidentDomainLogic zombieIncidentLogic;
    private IRepository<dtos.ZombieIncident> zombieRepository;
    private IRepository<dtos.GeoLocation> geoLocationRepository;
    private IUnitOfWork unitOfWork;
    private ZombieIncidentInfo zombieIncidentInfo;

    public SaveZombieIncidentTask(IZombieIncidentDomainLogic zombieIncidentLogic,
        IRepository<dtos.ZombieIncident> zombieRepository, 
    IRepository<dtos.GeoLocation> geoLocationRepository,
        IUnitOfWork unitOfWork)
    {
        this.zombieIncidentLogic = zombieIncidentLogic;
        this.zombieRepository = zombieRepository;
        this.geoLocationRepository = geoLocationRepository;
        this.unitOfWork = unitOfWork;
    }

    public SaveZombieIncidentResponse Handle(SaveZombieIncidentRequest request)
    {
        try
        {
            //get data
            this.zombieIncidentInfo = request.ZombieIncidentInfo;

            bool result = zombieIncidentLogic.CanStoreZombieIncident(zombieIncidentInfo);
            if (result)
            {
                ZombieIncident zombieIncident = ZombieIncidentDTOMapper.ToDTO(zombieIncidentInfo);

                zombieRepository.InsertOnSubmit(zombieIncident);
                zombieRepository.SubmitChanges();

                geoLocationRepository.InsertOnSubmit(zombieIncident.GeoLocation);
                geoLocationRepository.SubmitChanges();

                unitOfWork.Commit();
                unitOfWork.Dispose();
                return new SaveZombieIncidentResponse(result);
            }
            return new SaveZombieIncidentResponse(false);
        }
        catch (BusinessLogicException bex)
        {
            throw;
        }
        catch (Exception ex)
        {
            WcfExemplar.Common.Logging.LogManager.Instance.Logger("Tasks")
        .ErrorFormat("{0}\r\n{1}", ex.Message, ex.StackTrace);
            throw;
        }
    }
}

This simple Task, actually demonstrates quite a few things

  1. All its dependencies are satisfied by the IOC container, where these are fed in on the constructor
  2. The handling of a Request is done by implementing the IRequestHandler<TRequest,TResponse> interface, which allows the Task to recieve the Request on the Handle() method, where by it can grab the data from the Request
  3. It immediately delegates off to some business logic, which could be used within a common DLL which is shared between not only the WCF service, but could potentially be used by any code that needs to use common business logic for its shared objects. Though I have not gone that far with the attached demo code
  4. If the business logic says it is ok to save this entity, we ask the persistence layer (NHibernate) to do that in a proper Unit Of Work (Transactional) using NHibernate's ISession UOW object.

That is how I see a Task working, lean and mean and delegating to other single responsibility domain areas/services

Business Objects

The way I see things you may even be able to share your business objects if you get things right. You know things like validation rules, they sound like something that could be shared right?

If you allow the business objects to be shared along with things like Requests/WCF contracts/Fault contracts/Business logic, you can do all sorts of crazy stuff like including methods that might be useful to anyone in the enterprise.

Here is a typical business object from the demo app:

[DataContract]
public class ZombieIncidentInfo
{

    [DataMember]
    public int Id { get; set; }

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

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

    [DataMember]
    public GeoLocationInfo GeoLocation { get; set; }


    public ZombieIncidentInfo(string heading, string text, GeoLocationInfo geoLocation)
    {
        this.Heading = heading;
        this.Text = text;
        this.GeoLocation = geoLocation;
    }

    public ZombieIncidentInfo(int id, string heading, string text, GeoLocationInfo geoLocation)
    {
        this.Id = id;
        this.Heading = heading;
        this.Text = text;
        this.GeoLocation = geoLocation;
    }


    public bool IsValid()
    {
        return !string.IsNullOrEmpty(Heading) && Heading.Length <= 50
                && !string.IsNullOrEmpty(Text) && Text.Length <= 300;
    }
}

Some folk may look at this and go but hey I use WinForms/WPF/WinRT/Silverlight, why no INotifyPropertyChanged. Well for me the answer is that I would create a UI specific abstraction (ViewModel) for this.

Although very very simple I am sure you would all agree the IsValid() method does seem useful, and likely to be something more than 1 part of the enterprise may be interested in, so share it I say.

Business Logic

As previously stated I think if you get this stuff right, there may even be ways in which you can share business logic between many parts of the enterprise. Although this obviously is just a demo app, the code shown below is generic enough to work in a WPF/WinForms UI or Web UI, or another service, pretty much anywhere really. It's about re-use in my opinion.

public class ZombieIncidentDomainLogic : IZombieIncidentDomainLogic
{

    public bool CanStoreZombieIncident(ZombieIncidentInfo zombieIncident)
    {
        bool incidentValid = true;
        bool geoLocationValid = true;


        if (zombieIncident == null)
        {
            string error = ("ZombieIncidentDomainLogic.CanStoreZombieIncident : zombieIncident can not be null");
            WcfExemplar.Common.Logging.LogManager.Instance.Logger("Business Logic").Error(error);
            throw new BusinessLogicException("Zombie Incident data provided is empty");
        }

        if (zombieIncident.GeoLocation == null)
        {
            string error = ("ZombieIncidentDomainLogic.CanStoreZombieIncident : zombieIncident.GeoLocation can not be null");
            WcfExemplar.Common.Logging.LogManager.Instance.Logger("Business Logic").Error(error);
            throw new BusinessLogicException("GeoLocation data provided is empty");
        }

        if (!zombieIncident.IsValid())
        {
            incidentValid = false;
            string error = ("ZombieIncidentDomainLogic.CanStoreZombieIncident : " + 
               'zombieIncident has invalid data, ensure Heading/Text are filled in correctly");
            WcfExemplar.Common.Logging.LogManager.Instance.Logger("Business Logic").Error(error);
        }

        if (!zombieIncident.GeoLocation.IsValid())
        {
            geoLocationValid = false;
            string error = ("ZombieIncidentDomainLogic.CanStoreZombieIncident : " + 
               "geoLocation has invalid data, ensure Latitude/Longiture are filled in correctly");
            WcfExemplar.Common.Logging.LogManager.Instance.Logger("Business Logic").Error(error);
        }

        return incidentValid && geoLocationValid;

    }

    public bool IsValidSearch(string propertyName, SearchType searchType, string searchValue)
    {
        if (string.IsNullOrEmpty(propertyName))
        {
            string error = ("ZombieIncidentDomainLogic.IsValidSearch : propertyName can not be null");
            WcfExemplar.Common.Logging.LogManager.Instance.Logger("Business Logic").Error(error);
            throw new BusinessLogicException("Search for Zombie Incident is invalid. 'PropertyName' is empty");
        }

        if (string.IsNullOrEmpty(searchValue))
        {
            string error = ("ZombieIncidentDomainLogic.IsValidSearch : searchValue can not be null");
            WcfExemplar.Common.Logging.LogManager.Instance.Logger("Business Logic").Error(error);
            throw new BusinessLogicException("Search for Zombie Incident is invalid. 'SearchValue' is empty");
        }

        return true;
    }
}

Unit Of Work / Generic Repositories / Persistence

This articles demo code uses Fluent NHibernate as its Object Relational Mapper (ORM), which allows us to stay clear of SQL and just work with business objects or Data Transfer Objects (DTO). NHibernate comes with its own UnitOfWork which allows us to do things within transactional context. This is by using ISession. So we will make use of that.

In order to use Fluent NHibernate within the demo code, I wanted it to all be nice and swappable, so I do use elements of the IOC Container to ensure that the relevant NHibernate helper classes/ISession classes and Repositories are all obtained from the IOC Container.

We will look at all of these parts separately, but for now here is the relevant IOC Container setup that deals just with NHibernate:

public class StandardInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        string dbConnectionString = ConfigurationManager.ConnectionStrings["ZombieDB"].ConnectionString;

        container.AddFacility<WcfFacility>()
        .Register(
            Component.For<NHibernateHelper>().DependsOn(new
                    {
                        connectionString = dbConnectionString
                    }).LifeStyle.Singleton,
            Component.For<Func<ISessionFactory>>().Instance(() => 
              container.Resolve<NHibernateHelper>().SessionFactory).LifeStyle.Singleton,
            Component.For<Func<ISession>>().Instance(() => 
              ((INHibSessionProvider)container.Resolve<IUnitOfWork>()).Session).LifeStyle.Singleton,
            Component.For<IUnitOfWork>().ImplementedBy<NHibernateUnitOfWork>().LifeStyle.Singleton,
            Component.For<IRepository<ZombieIncident>>().ImplementedBy<NHibernateRepository<ZombieIncident>>().LifeStyle.Singleton,
            Component.For<IRepository<GeoLocation>>().ImplementedBy<NHibernateRepository<GeoLocation>>().LifeStyle.Singleton,
            ....
            ....
            ....

        );
    }
}

This is all pretty standard IOC behavior, apart from the Func delegates. These are simply factory delegates that are used to provide one of the values for other IOC registered components.

Unit Of Work: ISession

The Unit Of Work is realized by using the attached demo code NHibernateUnitOfWork class which is as follows:

public interface INHibSessionProvider
{
    ISession Session { get; }
}

public class NHibernateUnitOfWork : IUnitOfWork, INHibSessionProvider
{
    private ITransaction transaction;
    public ISession Session { get; private set; }

    public NHibernateUnitOfWork(Func<ISessionFactory> sessionFactory)
    {
        Session = sessionFactory().OpenSession();
        Session.FlushMode = FlushMode.Auto;
        this.transaction = Session.BeginTransaction(IsolationLevel.ReadCommitted);
    }


    public void Dispose()
    {
        if(Session.IsOpen)
        {
            Session.Close();
        }
    }

    public void Commit()
    {
        if(!transaction.IsActive)
        {
            throw new InvalidOperationException("No active transation");
        }
        transaction.Commit();
    }

    public void Rollback()
    {
        if(transaction.IsActive)
        {
            transaction.Rollback();
        }
    }
}

This class is pretty self explanatory, its simpy allows some work to be done within a single Transaction which is commited or rolled back.

It does however make use of another helper class, whch is provided by the IOC Container by using a Func delegate which act as factory.

public class NHibernateHelper
{

    private readonly string connectionString;
    private ISessionFactory sessionFactory;

    public ISessionFactory SessionFactory
    {
        get { return sessionFactory ?? (sessionFactory = CreateSessionFactory()); }
    }

    public NHibernateHelper(string connectionString)
    {
        this.connectionString = connectionString;
    }

    private ISessionFactory CreateSessionFactory()
    {
        return Fluently.Configure()
            .Database(MsSqlConfiguration.MsSql2008
                .ConnectionString(connectionString))
            .Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))
            .BuildSessionFactory();
    }
}

Generic Repositories

Another thing I tried to use was the Repository pattern, where I am using castles ability to support open generics.

Here is an example of the Repositories you will find in the attached code, where the NHibernate ISession (Unit Of Work) is passed to the Repository. This is done so that multiple operations across different Repositories, can all participate in the same Transaction. At least that is the idea. There will be times when you MUST call Commit() on the current Unit Of Work, which is fine, you can then just use your Repositories again with a new Transaction via the NHibernate ISession (Unit Of Work) which is passed to the Repository.

public class NHibernateRepository<T> : IRepository<T> where T : class
{

    private readonly ISession Session;

    public NHibernateRepository(Func<ISession> session)
    {
        Session = session();
    }

    public T GetById(int id)
    {
        return Session.Load<T>(id);
    }

    public IQueryable<T> GetAll()
    {
        return Session.Query<T>();
    }


    public T FindBy(System.Linq.Expressions.Expression<System.Func<T, bool>> expression)
    {
        return FilterBy(expression).Single();
    }

    public IQueryable<T> FilterBy(System.Linq.Expressions.Expression<System.Func<T, bool>> expression)
    {
        return GetAll().Where(expression).AsQueryable();
    }


    public void InsertOnSubmit(T entity)
    {
        Session.Save(entity);
    }

    public void DeleteOnSubmit(T entity)
    {
        Session.Delete(entity);
    }

    public void SubmitChanges()
    {
        Session.Flush();
    }
}

Here is an example showing you how the ISession based Unit Of Work and Repositories work together.

public SaveZombieIncidentTask(
    IZombieIncidentDomainLogic zombieIncidentLogic,
        IRepository<dtos.ZombieIncident> zombieRepository, 
    IRepository<dtos.GeoLocation> geoLocationRepository,
        IUnitOfWork unitOfWork, 
    ZombieIncidentInfo zombieIncidentInfo)
{
    this.zombieIncidentLogic = zombieIncidentLogic;
    this.zombieRepository = zombieRepository;
    this.geoLocationRepository = geoLocationRepository;
    this.unitOfWork = unitOfWork;
    this.zombieIncidentInfo = zombieIncidentInfo;
}

public override Response Execute()
{
    try
    {
        bool result = zombieIncidentLogic.CanStoreZombieIncident(zombieIncidentInfo);
        if (result)
        {
            ZombieIncident zombieIncident = ZombieIncidentDTOMapper.ToDTO(zombieIncidentInfo);
                    
            zombieRepository.InsertOnSubmit(zombieIncident);
            zombieRepository.SubmitChanges();
                    
            geoLocationRepository.InsertOnSubmit(zombieIncident.GeoLocation);
            geoLocationRepository.SubmitChanges();

            unitOfWork.Commit();
            unitOfWork.Dispose();
            return new SaveZombieIncidentResponse(result);
        }
        return new SaveZombieIncidentResponse(false);
    }
    catch (BusinessLogicException bex)
    {
        throw;
    }
    catch (Exception ex)
    {
        WcfExemplar.Common.Logging.LogManager.Instance.Logger("Tasks").ErrorFormat("{0}\r\n{1}",ex.Message, ex.StackTrace);
        throw;
    }
}

DTO Objects

Some of the eagle eyed amongst you, will notice that I do not use the shared WCF DataContract based objects above. I actually use some more light weight Data Transfer Objects (DTOs) which I use purely for talking with NHibernate and the database.

Here are the two DTOs used in the demo app project, see how it is quite clean and simply allows NHibernate to populate properties. One thing I decided to introduce was a PersistableBase class that dealt with common things that a peristable entity may need such as

  • Id
  • Version (Optimistic concurrency)
public abstract class PersistableBase
{
    public virtual int Id { get; set; }
    public virtual byte[] Version { get; set; }
}

public class ZombieIncident : PersistableBase
{
    public virtual string Heading { get; set; }
    public virtual string Text { get; set; }
    public virtual GeoLocation GeoLocation { get; set; }
}

public class GeoLocation
{
    private int ZombieIncidentId { get; set; }
    private ZombieIncident ZombieIncident { get; set; }

    protected GeoLocation() 
    {
    }
        
    public GeoLocation(ZombieIncident zombieIncident)
    {
        ZombieIncident = zombieIncident;
    }

    public virtual double Latitude { get; set; }
    public virtual double Longitude { get; set; }
}

You may be wondering how these guys get populated, well the answer to that when using Fluent NHibernate, lies in the use of Mapper files. Here are the two mapper files for the two DTOs you have just seen (where I also use a base mapper class called PersistanceMapping<TDomainEntity>.

public abstract class PersistenceMapping<TDomainEntity> : 
    ClassMap<TDomainEntity> where TDomainEntity : PersistableBase
{
    public PersistenceMapping()
    {
        Id(x => x.Id).GeneratedBy.Identity();
        OptimisticLock.Version();
        Version(x => x.Version).Column("Version").Generated.Always();
            
    }
}


public class ZombieIncidentMap : PersistenceMapping<ZombieIncident>
{
    public ZombieIncidentMap() : base()
    {
        Table("ZombieIncidents");
        Map(x => x.Heading);
        Map(x => x.Text);
        HasOne(x => x.GeoLocation).Cascade.All();
    }
}


public class GeoLocationMap : ClassMap<GeoLocation>
{
    public GeoLocationMap()
    {
        Table("GeoLocations");
        Id(Reveal.Property<GeoLocation>("ZombieIncidentId")).GeneratedBy.Foreign("ZombieIncident");
        HasOne(Reveal.Property<GeoLocation, ZombieIncident>("ZombieIncident")).Constrained().ForeignKey();
        Map(x => x.Latitude);
        Map(x => x.Longitude);
    }
}

Fluent HHibernate One To One Mapping

The two2 Fluent NHibernate files above are used to create a ZombieIncident with a 1 to 1 relationship with a GeoLocation object. This sounds trivial but it turned out to be way harder than I thought, so I started googling, and found the following link which was brilliant, and it was I used to get this working.

So that about concludes the server side code, a lot of this may not sink in until you examine the code for yourself.

Next we will talk about the UI. Before we do that, let me just say this article is more about trying to come up with a good service side design, the UI is merely a vehicle for calling the service side code.

However I thought it may be a good time to also give several people who use my Cinch MVVM framework a richer example, and try creating a Metro'ish app, so I did just that, and I think the demo UI code you see attached is not bad for showcasing my Cinch MVVM framework and PRISM working together.

Although the UI code is full featured and a rich example of using MVVM and PRISM, I will not be spending too much time going through its internals. I will ONLY talk about the salient points and you can look at the codeor various other articles I have written around Cinch, or check the PRISM documentation should you want more.

The Client Side: WPF

As just stated the client is a WPF app which makes use of the following:

  • Cinch: My own MVVM Framework, you can read more about that cinch.codeplex.com
  • PRISM: Which is the modular framework which just happens to work quite nicely with my Cinch library

If you are not familiar with either of these 2 things, I would suggest you look at the cinch.codeplex.com and follow the Version 2 article links there. As for PRISM, I would suggest you read the documentation (which is very good) that is avaiable from the PRISM web site.

So What Does the UI Actually Do

Here is a recap of what the UI allows the user to do:

  • Search for items (I am storing Zombie data where a heading/description and GeoLocation data are stored for each Zombie incident)
  • Add new ZombieIncident
  • View ZombieIncident items on a map (I have chosen to use Bing maps)
  • Use Rx to show an RSS feed of global Zombie incidents (as a bonus if you will)

The UI is obvisouly MVVM and makes heavy use of the following PRISM features

  • Modules
  • Regions

The are four main regions/modules which do the following:

  1. ShellModule: Provides a module that deals with the commands for the MainWindow
  2. MapModule: Provides a module that shows zombie incidents on a 2D Bing map, and allows new zombie incidents to be created
  3. RSSFeedModule: Provides a module that will grab zombie incidents of a zombie web site using Reactive Framework (Rx)
  4. SearchModule: Provides a module that allows users to search zombie incidents, and will display the results in a Panorama control and also allows users to view these incidents in more detail

Important note

The UI uses Bing maps, so make sure you obtain your own API key, and do what is mentioned in the pre-requisites section at the top of this article. Also as this UI uses Bing Maps (and also RX to grab some zombie data from an RSS feed) you must have a good internet connection to make the app work as expected.

Shared DLLs

I think one of the key things that I see people doing when they first started working with WCF was to use a service reference.cs file. Which is either created by using the Visual Studio "Add Service Reference" menu item, or is created using SvcUtil.exe. The problem with using Reference.cs is that you will only get to see any DataContract/DataMembers serialized but no constructors and methods will get serialized, which is pretty crap really.

A better approach is to simply separate out things that are common between a WCF service and clients of the WCF service. So typically this would be things like

  • DataContract business objects
  • ServiceContract (the WCF service interface)
  • FaultContracts, such that the WCF clients can handle faults raised by the WCF service

For the demo app the following two DLLs are shared between the WCF service and the WPF client. Hopefully it should be obvious what is being shared by the names of the folders in the two screenshots shown below.

WcfExamplar.Contracts

WcfExemplar.Common

By sharing this DLL the WPF client is able to do completely insane things like call actual methods in objects in the shared DLL. Another totally way out there thing, is that the WPF client can even use constructors for the shared business objects.Mental.

Now if you have of used Reference.cs (generated for you by either Visual Studio or SvcUtil.exe) you would not have gotten any methods. The long and short of it, never use Reference.cs, it is evil and gives you nothing. Just share the common stuff in DLLs. It gives you everything Reference.cs gives you and more. Such as the ability to know how to construct your objects in a valid state, instead of guessing what properties you may need to set to successfully create a object you may find in Reference.cs.

Common Proxy

To facilitate communications with the WCF service the WPF client uses a simple proxy class which is shown below:

[PartCreationPolicy(CreationPolicy.Shared)]
[Export(typeof(IServiceInvoker))]
public class WCFServiceInvoker : IServiceInvoker
{
    private static ChannelFactoryManager _factoryManager = new ChannelFactoryManager();
    private static ClientSection _clientSection = 
      ConfigurationManager.GetSection("system.serviceModel/client") as ClientSection;

    public R CallService<R>(Request request) where R : Response
    {
        var endpointNameAddressPair = GetEndpointNameAddressPair(typeof(IGateway));
        IGateway proxy = _factoryManager.CreateChannel<IGateway>(endpointNameAddressPair.Key, endpointNameAddressPair.Value);
        ICommunicationObject commObj = (ICommunicationObject)proxy;
        try
        {
            return (R)proxy.ExecuteRequest(request);
        }
        catch (FaultException<GenericFault> gf)
        {
            WcfExemplar.Common.Logging.LogManager.Instance.Logger(
        "WCFServiceInvoker").Error("A Gateway FaultException<GenericFault> occured", gf.InnerException);
            throw new ApplicationException("A Gateway FaultException<GenericFault> occured", gf.InnerException);
        }
        catch (FaultException<BusinessLogicFault> bf)
        {
            WcfExemplar.Common.Logging.LogManager.Instance.Logger(
        "WCFServiceInvoker").Error("A Gateway FaultException<BusinessLogicFault> occured", bf.InnerException);
            throw new BusinessLogicException(bf.Message);
        }
        catch (Exception ex)
        {
            WcfExemplar.Common.Logging.LogManager.Instance.Logger(
        "WCFServiceInvoker").Error("A Gateway Exception occured", ex);
            throw new Exception("A Gateway Exception occured", ex);
        }
        finally
        {
            try
            {
                if (commObj.State != CommunicationState.Faulted)
                {
                    commObj.Close();
                }
            }
            catch
            {
                commObj.Abort();
            }
        }
    }

    private static KeyValuePair<string, string> GetEndpointNameAddressPair(Type serviceContractType)
    {
        var configException = new ConfigurationErrorsException(string.Format(
        "No client endpoint found for type {0}. Please add the section <client>" + 
        "<endpoint name=\"myservice\" address=\"http://address/\" binding" + 
        "=\"basicHttpBinding\" contract=\"{0}\"/></client> in the config file.", 
        serviceContractType));

        if (((_clientSection == null) || (_clientSection.Endpoints == null)) || (_clientSection.Endpoints.Count < 1))
        {
            throw configException;
        }
        foreach (ChannelEndpointElement element in _clientSection.Endpoints)
        {
            if (element.Contract == serviceContractType.ToString())
            {
                return new KeyValuePair<string, string>(element.Name, element.Address.AbsoluteUri);
            }
        }
        throw configException;
    }

}

Some of the salient points of this helper class are

  • It catches the known FaultContract(s) and logs them
  • It is also capable of escalating certain Exception(s) to the calling WPF client code, such that it can be shown to users
  • It will locate the address for the contract from the App.Config automatically

This helper proxy class also makes use of another helper class that deals with ChannelFactory creation. This extra helper classis shown below:

public class ChannelFactoryManager : IDisposable
{
    private static Dictionary<Type, ChannelFactory> factories = new Dictionary<Type, ChannelFactory>();
    private static readonly object _syncRoot = new object();

    public virtual T CreateChannel<T>() where T : class
    {
        return CreateChannel<T>("*", null);
    }

    public virtual T CreateChannel<T>(string endpointConfigurationName) where T : class
    {
        return CreateChannel<T>(endpointConfigurationName, null);
    }

    public virtual T CreateChannel<T>(
        string endpointConfigurationName, string endpointAddress) where T : class
    {
        T local = GetFactory<T>(endpointConfigurationName, endpointAddress).CreateChannel();
        ((IClientChannel)local).Faulted += ChannelFaulted;
        return local;
    }

    protected virtual ChannelFactory<T> GetFactory<T>(
        string endpointConfigurationName, string endpointAddress) where T : class
    {
        lock (_syncRoot)
        {
            ChannelFactory factory;
            if (!factories.TryGetValue(typeof(T), out factory))
            {
                factory = CreateFactoryInstance<T>(endpointConfigurationName, endpointAddress);
                factories.Add(typeof(T), factory);
            }
            return (factory as ChannelFactory<T>);
        }
    }

    private ChannelFactory CreateFactoryInstance<T>(
        string endpointConfigurationName, string endpointAddress)
    {
        ChannelFactory factory = null;
        if (!string.IsNullOrEmpty(endpointAddress))
        {
            factory = new ChannelFactory<T>(
                endpointConfigurationName, new EndpointAddress(endpointAddress));
        }
        else
        {
            factory = new ChannelFactory<T>(endpointConfigurationName);
        }
        factory.Faulted += FactoryFaulted;
        factory.Open();
        return factory;
    }

    private void ChannelFaulted(object sender, EventArgs e)
    {
        IClientChannel channel = (IClientChannel)sender;
        try
        {
            channel.Close();
        }
        catch
        {
            channel.Abort();
        }
        throw new ApplicationException("Exc_ChannelFailure");
    }

    private void FactoryFaulted(object sender, EventArgs args)
    {
        ChannelFactory factory = (ChannelFactory)sender;
        try
        {
            factory.Close();
        }
        catch
        {
            factory.Abort();
        }
        Type[] genericArguments = factory.GetType().GetGenericArguments();
        if ((genericArguments != null) && (genericArguments.Length == 1))
        {
            Type key = genericArguments[0];
            if (factories.ContainsKey(key))
            {
                factories.Remove(key);
            }
        }
        throw new ApplicationException("Exc_ChannelFactoryFailure");
    }

    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            lock (_syncRoot)
            {
                foreach (Type type in factories.Keys)
                {
                    ChannelFactory factory = factories[type];
                    try
                    {
                        factory.Close();
                        continue;
                    }
                    catch
                    {
                        factory.Abort();
                        continue;
                    }
                }
                factories.Clear();
            }
        }
    }
}

Some of the salient points of this helper class are

  • It holds a Dictionary of ChannelFactory instances that are not Faulted. Though in this demo codes case this is only going to contain one key, as there is only one service
  • It will automatically remove any Faulted ChannelFactory

I actually got the basics of both of these classes from a StackOverflow forum entry which you can read more abut here : http://stackoverflow.com/questions/3200197/creating-wcf-channelfactoryt

Services to Talk To The WCF Service

In order to talk to the WCF service the WPF client makes use of services. These services are typically abstracted away behind an interface. This is done in order to facilitate better testing to some extent. As if you your WCF service is not avaiable, we could feed in some mock services that could talk to some in memory store rather than directly to the WCF service.

Here is a typical service:

namespace WcfExemplar.WpfClient.RSSFeedModule.Services
{
    public interface IRssFeedProvider
    {
        void LoadFeed (Action<IEnumerable<Item>> subscribeCallback, Action<Exception> errorCallback);
    }
}

namespace WcfExemplar.WpfClient.MapModule.Services
{
    public interface IZombieIncidentProvider
    {
        bool Save(ZombieIncidentViewModel newIncident);
        void LoadIncidents(Action<IEnumerable<ZombieIncidentViewModel>> successCallback, Action<Exception> errorCallback);
    }
}


namespace WcfExemplar.WpfClient.SearchModule.Services
{
    public enum SearchType
    {
        Contains = 1,
        StartsWith = 2,
        EndsWith = 3,
        ShowAll = 4
    };

    public interface ISearchProvider
    {
        void SearchIncidents(
            string propertyName,
            SearchType searchType,
            string searchValue,
            Action<IEnumerable<PanoramaZombieIncidentViewModel>> successCallback, Action<Exception> errorCallback);
    }
}

Some of you may notice that these service methods don't return any results directly. The reason for this is that we want to keep the UI responsive.

All such UI services are asynchronous (either using Reactive Extensions (RX) or use Task Parallel Library (TPL)) to ensure the UI is kept responsive such that it may do things like o animate a busy indicator while some background work is happening. The Action<T> delegates, serve as callbacks when either the work is done or an error has occurred.

Another thing you may notice (if you examine the code base), is that these services are made available to the ViewModels in the demo app by using MEF. That is what you get for free if you use Cinch V2 and PRISM 4 together. They work very well together indeed.

We will be talking more about the services used in the 3 sub-sections shown below. 

RSS Feed Module

Important note:

You can click on the images below to view a bigger copy of the image

The RSS feed module reads data from the following URL: http://www.zombiereportingcenter.com/feed/ using the  Reactive Extensions (RX).

Where you can click on the "View full RSS feed" button to view the full RSS Feed, which is as shown below.

This is a friction enabled ScrollViewer which I have talked about before, so click and release to have some friction fun.

The main work to produce the data that drives these screens is done by the RssFeedProvider, which is as follows:

[PartCreationPolicy(CreationPolicy.NonShared)]
[ExportService(ServiceType.Runtime, typeof(IRssFeedProvider))]
public class RssFeedProvider : IRssFeedProvider
{
    private string feedUri = "http://www.zombiereportingcenter.com/feed/";


    public void LoadFeed(Action<IEnumerable<Item>> subscribeCallback, Action<Exception> errorCallback)
    {
        Func<IObservable<string>> readZombieFeed = () =>
        {
            var request = (HttpWebRequest)HttpWebRequest.Create(new Uri(feedUri));
            var zombieFeedAsyncObs = Observable.FromAsyncPattern<WebResponse>(request.BeginGetResponse, request.EndGetResponse);
            return zombieFeedAsyncObs().Select(res => WebResponseToString(res));
        };

        var sub = Observable.Interval(TimeSpan.FromSeconds(5))
            .SelectMany(txt => readZombieFeed())
            .Select(response => ParseZombieFeedData(response))
            .Subscribe(zombieResults => subscribeCallback(FilterResults(zombieResults)), ex => errorCallback(ex));
    }


    private IEnumerable<Item> FilterResults(IEnumerable<Channel> zombieResults)
    {
        List<Item> items = new List<Item>();
        foreach (Channel channel in zombieResults)
        {
            items.AddRange(channel.Items);
        }
        return items;
    }


    private string WebResponseToString(WebResponse webResponse)
    {
        HttpWebResponse response = (HttpWebResponse)webResponse;
        using (StreamReader reader = new StreamReader(response.GetResponseStream()))
        {
            return reader.ReadToEnd();
        }
    }


    private IEnumerable<Channel> ParseZombieFeedData(string response)
    {
        var xdoc = XDocument.Parse(response);
        return from channels in xdoc.Descendants("channel")
            select new Channel
            {
                Title = channels.Element("title") != null ? channels.Element("title").Value : "",
                Link = channels.Element("link") != null ? channels.Element("link").Value : "",
                Description = channels.Element("description") != null ? channels.Element("description").Value : "",
                Items = from items in channels.Descendants("item")
                    select new Item
                    {
                        Title = items.Element("title") != null ? items.Element("title").Value : "",
                        Link = items.Element("link") != null ? items.Element("link").Value : "",
                        Description = items.Element("description") != null ? items.Element("description").Value : "",
                        Guid = (items.Element("guid") != null ? items.Element("guid").Value : "")
                    }
            };
    }

}

It can be seen that this does several things in order to get results

  1. It creates an Observable from an asynchronous web request, which it refreshes using the Rx Interval method
  2. The data is then parsed using standard XLINQ operations, into Channel/Item objects which you can see in the code
  3. As all this module does it parse some results from a web site, there is no need for WCF in this module, it is all nice and self contained

MapModule

This module shows all existing zombie incidents on a Bing map. Just for the record if you followed the "Getting Started" section you should have some existing zombie incidents as I gave you some dummy data to seed the database with.

Here is what it possible with this module

  1. You can zoom the map using the controls on the right
  2. You can change the type of map using the controls on the right
  3. View tooltips for existing zombie incidents by hovering over the zombie icons shown
  4. When you double click on a location on the map, a new zombie panel will slide in from the right which will allow you to create new zombie incidents (this is shown below)

Let's break down the things this module does and examine the code in more detail:

Working With Bing Maps

The Bing map is available as a standard WPF control, which means you literally just have to put something like this in your XAML.

<!-- CredentialsProvider="INSERT_YOUR_BING_MAPS_KEY" -->
<bing:Map x:Name="map"
            Grid.Row="1"
            Grid.Column="0"
            PreviewMouseDoubleClick="Map_PreviewMouseDoubleClick"
            CredentialsProvider="INSERT_YOUR_BING_MAPS_KEY"
            ZoomLevel="4.0"
            AnimationLevel="Full"
            Mode="AerialWithLabels"
            Center="37.806029,-122.407007" />

So with the map in place, adding data, and controlling the map is easily achieved using some simple code behind (Yes even I a MVVM pusher, use code behind occasionally where I see it makes sense). Here is the entire code behind for the ZombieIncidentMapView.

[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public partial class ZombieIncidentMapView : UserControl, IZombieIncidentMapView
{
    private double minZoom = 0;
    private double maxZoom = 20;

    LocationConverter locConverter = new LocationConverter();

    public ZombieIncidentMapView()
    {
        InitializeComponent();
        map.Focus();
        Cinch.Mediator.Instance.Register(this);
    }

    public void CreatePinsForZombieIncidents(IEnumerable<ZombieIncidentViewModel> zombieIncidents)
    {
        map.Children.Clear();
        foreach (var zombieIncident in zombieIncidents)
        {
            MapPin pin = new MapPin() { ZombieIncident = zombieIncident };
            pin.SetValue(MapLayer.PositionProperty, new Location(zombieIncident.Latitude, zombieIncident.Longitude, 0));
            pin.SetValue(MapLayer.PositionOriginProperty, PositionOrigin.Center);
            map.Children.Add(pin);
        }
    }


    public void AddNewlyCreatedIncident(ZombieIncidentViewModel zombieIncident)
    {
        VisualStateManager.GoToState(this, "HideAddState", true);
        MapPin pin = new MapPin() { ZombieIncident = zombieIncident };
        pin.SetValue(MapLayer.PositionProperty, new Location(zombieIncident.Latitude, zombieIncident.Longitude, 0));
        pin.SetValue(MapLayer.PositionOriginProperty, PositionOrigin.Center);
        map.Children.Add(pin);

    }
 
    [Cinch.MediatorMessageSink("HideAddIncidentPaneMessage")]
    public void OnHideAddIncidentPaneMessage(bool dummy)
    {
        VisualStateManager.GoToState(this, "HideAddState", true);
    }

        
    private void Map_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        e.Handled = true;
        Point mousePosition = e.GetPosition(this);
        Location pinLocation = map.ViewportPointToLocation(mousePosition);

        addNewGrid.Content = new AddNewIncidentView() 
            { 
                GeoLocation = new Tuple<double, double>(pinLocation.Latitude, pinLocation.Longitude) 
            };
        //Shows add new zombie incident panel or RHS of screen
        VisualStateManager.GoToState(this, "ShowAddState", true);
    }

    private void btnZoomIn_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            var zoom = map.ZoomLevel + 2;
            map.ZoomLevel = zoom > maxZoom ? maxZoom : zoom;
        }
        //map bad
        catch { }
    }

    private void btnZoomOut_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            var zoom = map.ZoomLevel - 2;
            map.ZoomLevel = zoom < minZoom ? minZoom : zoom;
        }
        //map bad
        catch { }
    }

    private void ContextMenu_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            MenuItem menuItem = (MenuItem)e.OriginalSource;
            switch (menuItem.Header.ToString())
            {
                case "Aerial":
                    map.Mode = new AerialMode(true);
                    break;
                case "Road":
                    map.Mode = new RoadMode();
                    break;
            }
        }
        //map bad
        catch { }
    }

    private void btnMapType_Click(object sender, RoutedEventArgs e)
    {
        (sender as Button).ContextMenu.IsOpen = true;
    }
}

It is pretty straight forward, and I hope that you can see this code simply manipulates the Bing map control, and allows the displaying of existing zombie incidents/ allows the adding of a new zombie incident via a new view which is simply shown/hidden from this view.

Obtaining All Existing Zombie Incidents

In order to fetch ALL existing zombie incidents from the database the following WPF UI service is used

public interface IZombieIncidentProvider
{
    bool Save(ZombieIncidentViewModel newIncident);
    void LoadIncidents(Action<IEnumerable<ZombieIncidentViewModel>> successCallback, Action<Exception> errorCallback);
}

Where obviously we need to focus on the LoadIncidents(..) method. Which is as follows:

[PartCreationPolicy(CreationPolicy.NonShared)]
[ExportService(ServiceType.Runtime, typeof(IZombieIncidentProvider))]
public class ZombieIncidentProvider : IZombieIncidentProvider
{
    private IServiceInvoker serviceInvoker;

    [ImportingConstructor]
    public ZombieIncidentProvider(IServiceInvoker serviceInvoker)
    {
        this.serviceInvoker = serviceInvoker;
    }
        
 
    public void LoadIncidents(Action<IEnumerable<ZombieIncidentViewModel>> 
                successCallback, Action<Exception> errorCallback)
    {
        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        CancellationToken cancellationToken = cancellationTokenSource.Token;
        Task<bool> cancellationDelayTask = TaskHelper.CreateDelayTask(20000);
        cancellationDelayTask.ContinueWith(dt =>
        {
            cancellationTokenSource.Cancel();
        }, TaskContinuationOptions.OnlyOnRanToCompletion);

        try
        {
            Task<IEnumerable<ZombieIncidentViewModel>> searchTask = 
               Task.Factory.StartNew<IEnumerable<ZombieIncidentViewModel>>(() =>
            {
                var incidents = this.serviceInvoker.CallService<ZombieIncidentsResponse>(
                    new ZombieIncidentsRequest()).ZombieIncidents.ToList();
                List<ZombieIncidentViewModel> zombieIncidentViewModels = new List<ZombieIncidentViewModel>();
                foreach (var incident in incidents)
                {
                    zombieIncidentViewModels.Add(new ZombieIncidentViewModel(
                      incident.GeoLocation.Latitude, incident.GeoLocation.Longitude, incident.Heading, incident.Text));
                }

                return zombieIncidentViewModels;
            }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);

            //Sucess callback
            searchTask.ContinueWith(ant =>
            {
                successCallback(ant.Result); 
            }, cancellationToken, TaskContinuationOptions.OnlyOnRanToCompletion, 
                  TaskScheduler.FromCurrentSynchronizationContext());

            //Failure callback
            searchTask.ContinueWith(ant =>
            {
                LogManager.Instance.Logger("WpfClient.MapModule.ZombieIncidentProvider").Error(
                     "A timeout occurred whilst attempting to fetch the zombie incidents");
                errorCallback(new TimeoutException("A timeout occurred whilst attempting to fetch the zombie incidents"));
            }, cancellationToken, TaskContinuationOptions.NotOnRanToCompletion, 
                  TaskScheduler.FromCurrentSynchronizationContext());
        }
        catch(AggregateException ex)
        {
            LogManager.Instance.Logger("WpfClient.MapModule.ZombieIncidentProvider").Error(ex.Flatten().Message);
            errorCallback(new ApplicationException("A generic error occurred whilst trying to fetch the zombie incidents"));
        }
    }
}

The code above demonstrates several things that we will see again

  1. We use TPL to ensure this work is done using the ThreadPool, to ensure the UI stay responsive whilst this work is happening
  2. We make use of the Service proxy class that we talked about earlier
  3. We make use of callback Action<T> to call on success and on failure
  4. We create a new delay Task that will cancel the actual Task if it has not completed within x amount of time

So that is how the UI works. Let's now move our attention onto the WCF Task that actual provides the existing zombie incidents shall we.

The WCF Task ZombieIncidentsTask is where this happens. Here is the entire class, which I think its very self explanatory, now that we know that we are using Unit Of Work / Repository pattern and make use of Fluent NHibernate.

public class ZombieIncidentsTask : Task
{
    private IRepository<dtos.ZombieIncident> zombieRepository;
    private IUnitOfWork unitOfWork;

    public ZombieIncidentsTask(IRepository<dtos.ZombieIncident> customerRepository, IUnitOfWork unitOfWork)
    {
        this.zombieRepository = customerRepository;
        this.unitOfWork = unitOfWork;
    }

    public override Response Execute()
    {
        try
        {
            List<ZombieIncidentInfo> zombies = new List<ZombieIncidentInfo>();
            foreach (dtos.ZombieIncident zombie in zombieRepository.GetAll().ToList())
            {
                zombies.Add(ZombieIncidentDTOMapper.FromDTO(zombie));
            }
            return new ZombieIncidentsResponse(zombies);
        }
        catch (Exception ex)
        {
            return new ZombieIncidentsResponse(new List<ZombieIncidentInfo>());
        }
    }
}

Saving a New Zombie Incident

In order to create a new zombie incident within the database the following WPF UI service is used

public interface IZombieIncidentProvider
{
    bool Save(ZombieIncidentViewModel newIncident);
    void LoadIncidents(Action<IEnumerable<ZombieIncidentViewModel>> successCallback, Action<Exception> errorCallback);
}

Where obviously we need to focus on the Save(..) method. Which is as follows:

[PartCreationPolicy(CreationPolicy.NonShared)]
[ExportService(ServiceType.Runtime, typeof(IZombieIncidentProvider))]
public class ZombieIncidentProvider : IZombieIncidentProvider
{
    private IServiceInvoker serviceInvoker;

    [ImportingConstructor]
    public ZombieIncidentProvider(IServiceInvoker serviceInvoker)
    {
        this.serviceInvoker = serviceInvoker;
    }
        
    public bool Save(ZombieIncidentViewModel newIncident)
    {
        ZombieIncidentInfo zombieIncidentInfo= new ZombieIncidentInfo(newIncident.Heading, newIncident.Text,
            new GeoLocationInfo(newIncident.Latitude,newIncident.Longitude));
        return this.serviceInvoker.CallService<SaveZombieIncidentResponse>(
        new SaveZombieIncidentRequest(zombieIncidentInfo)).Success;
    }
 }

As before we make use of the Service proxy class that we talked about earlier.

So that is how the UI works. Let's now move our attention onto the WCF task that actual provides the existing zombie incidents, shall we?

The WCF Task SaveZombieIncidentTask is where this happens.

public class SaveZombieIncidentTask : Task
{
    private IZombieIncidentDomainLogic zombieIncidentLogic;
    private IRepository<dtos.ZombieIncident> zombieRepository;
    private IRepository<dtos.GeoLocation> geoLocationRepository;
    private IUnitOfWork unitOfWork;
    private ZombieIncidentInfo zombieIncidentInfo;

    public SaveZombieIncidentTask(IZombieIncidentDomainLogic zombieIncidentLogic,
        IRepository<dtos.ZombieIncident> zombieRepository, IRepository<dtos.GeoLocation> geoLocationRepository,
        IUnitOfWork unitOfWork, ZombieIncidentInfo zombieIncidentInfo)
    {
        this.zombieIncidentLogic = zombieIncidentLogic;
        this.zombieRepository = zombieRepository;
        this.geoLocationRepository = geoLocationRepository;
        this.unitOfWork = unitOfWork;
        this.zombieIncidentInfo = zombieIncidentInfo;
    }

    public override Response Execute()
    {
        try
        {
            bool result = zombieIncidentLogic.CanStoreZombieIncident(zombieIncidentInfo);
            if (result)
            {
                ZombieIncident zombieIncident = ZombieIncidentDTOMapper.ToDTO(zombieIncidentInfo);
                    
                zombieRepository.InsertOnSubmit(zombieIncident);
                zombieRepository.SubmitChanges();
                    
                geoLocationRepository.InsertOnSubmit(zombieIncident.GeoLocation);
                geoLocationRepository.SubmitChanges();

                unitOfWork.Commit();
                unitOfWork.Dispose();
                return new SaveZombieIncidentResponse(result);
            }
            return new SaveZombieIncidentResponse(false);
        }
        catch (BusinessLogicException bex)
        {
            throw;
        }
        catch (Exception ex)
        {
            WcfExemplar.Common.Logging.LogManager.Instance.Logger(
                 "Tasks").ErrorFormat("{0}\r\n{1}",ex.Message, ex.StackTrace);
            throw;
        }
    }
}

As before we simply make use of the things we have already seen:

  1. Business logic: To ensure the new zombie business object is valid for saving
  2. DTO: Transfer objects to write to the database
  3. Unit of work: Allows work to be done one transaction
  4. Repository: Allows us to abstract away the operations with the database, we simply add to a repository

Search Module

This module allows the user to craft a search, which is used to view matching zombie incidents in a tile Panorama control, which is a control I constructed in a previous article.

As can be seen from the above diagram you have several options to allow you to craft your search

  • You may pick search property
    • Heading
    • Text
  • You may pick search type (where some of these will also need a search value)
    • Contains
    • StartsWith
    • EndsWith
    • ShowAll (default)

Whilst some background operation is happening in the background the UI will show this animated spinner

Here is what the results look like when they arrive

When you click on one of the tiles an information panel will animate in from the RHS of the screen, which shows you some text and also a mini map.

Here is the panel showing the data as text

Here is the panel showing the data on a mini map

In order to search for zombie incident within the database the following WPF UI service is used

public enum SearchType
{
    Contains = 1,
    StartsWith = 2,
    EndsWith = 3,
    ShowAll = 4
};

public interface ISearchProvider
{
    void SearchIncidents(
        string propertyName,
        SearchType searchType,
        string searchValue,
        Action<IEnumerable<PanoramaZombieIncidentViewModel>> successCallback, Action<Exception> errorCallback);
}

Where the actual code service code is as follows:

[PartCreationPolicy(CreationPolicy.NonShared)]
[ExportService(ServiceType.Runtime, typeof(ISearchProvider))]
public class SearchProvider : ISearchProvider
{
    private IServiceInvoker serviceInvoker;

    [ImportingConstructor]
    public SearchProvider(IServiceInvoker serviceInvoker)
    {
        this.serviceInvoker = serviceInvoker;
    }

    public void SearchIncidents(string propertyName, SearchType searchType, string searchValue,
        Action<IEnumerable<PanoramaZombieIncidentViewModel>> successCallback, Action<Exception> errorCallback)
    {
        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        CancellationToken cancellationToken = cancellationTokenSource.Token;
        Task<bool> cancellationDelayTask = TaskHelper.CreateDelayTask(20000);
        cancellationDelayTask.ContinueWith(dt =>
        {
            cancellationTokenSource.Cancel();
        }, TaskContinuationOptions.OnlyOnRanToCompletion);

        try
        {

            Task<IEnumerable<PanoramaZombieIncidentViewModel>> searchTask = 
               Task.Factory.StartNew<IEnumerable<PanoramaZombieIncidentViewModel>>(() =>
            {
                SearchZombieIncidentsRequest searchZombieIncidentsRequest = 
                  new SearchZombieIncidentsRequest(propertyName, 
                  (WcfExamplar.Contracts.SearchType)searchType, searchValue);

                var incidents = this.serviceInvoker.CallService<SearchZombieIncidentsResponse>(
                  searchZombieIncidentsRequest).ZombieIncidents.ToList();
                List<PanoramaZombieIncidentViewModel> zombieIncidentViewModels = new List<PanoramaZombieIncidentViewModel>();
                foreach (var incident in incidents)
                {
                    zombieIncidentViewModels.Add(new PanoramaZombieIncidentViewModel(
                      incident.GeoLocation.Latitude, incident.GeoLocation.Longitude, incident.Heading, incident.Text));
                }

                return zombieIncidentViewModels;
            }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);

            //Sucess callback
            searchTask.ContinueWith(ant =>
            {
                successCallback(ant.Result);
            }, cancellationToken, TaskContinuationOptions.OnlyOnRanToCompletion, 
                  TaskScheduler.FromCurrentSynchronizationContext());

            //Failure callback
            searchTask.ContinueWith(ant =>
            {
                LogManager.Instance.Logger("WpfClient.SearchModule.SearchProvider").Error(
                  "A timeout occurred whilst attempting to search for zombie incidents");
                errorCallback(new TimeoutException("A timeout occurred whilst attempting to search for zombie incidents"));
            }, cancellationToken, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
        }
        catch (AggregateException ex)
        {
            LogManager.Instance.Logger("WpfClient.SearchModule.SearchProvider").Error(ex.Flatten().Message);
            errorCallback(new ApplicationException("A generic error occurred whilst trying to search for zombie incidents"));
        }
    }
}

As before we make use of the Service proxy class that we talked about earlier.

So that is how the UI works. Let's now move our attention onto the WCF task that actual provides the existing zombie incidents, shall we?

The WCF Task "SearchZombieIncidentsTask" is where this happens.

public class SearchZombieIncidentsTask : Task
{
    private IZombieIncidentDomainLogic zombieIncidentLogic;
    private IRepository<dtos.ZombieIncident> zombieRepository;
    private IUnitOfWork unitOfWork;
    private string propertyName;
    private SearchType searchType;
    private string searchValue;


    public SearchZombieIncidentsTask(IZombieIncidentDomainLogic zombieIncidentLogic,
        IRepository<dtos.ZombieIncident> zombieRepository,
        IUnitOfWork unitOfWork, string propertyName, SearchType searchType, string searchValue)
    {
        this.zombieIncidentLogic = zombieIncidentLogic;
        this.zombieRepository = zombieRepository;
        this.unitOfWork = unitOfWork;
        this.propertyName = propertyName;
        this.searchType = searchType;
        this.searchValue = searchValue;
    }

    public override Response Execute()
    {
        try
        {
            List<ZombieIncidentInfo> zombies = new List<ZombieIncidentInfo>();

            if (searchType == SearchType.ShowAll)
            {

                foreach (dtos.ZombieIncident zombie in zombieRepository.GetAll().ToList())
                {
                    zombies.Add(ZombieIncidentDTOMapper.FromDTO(zombie));
                }
            }
            else
            {
                bool result = zombieIncidentLogic.IsValidSearch(propertyName, searchType, searchValue);
                if (result)
                {
                    var paramStart = Expression.Parameter(typeof(dtos.ZombieIncident), "x");
                    Expression<Func<dtos.ZombieIncident, bool>> searchExp = Expression.Lambda<Func<dtos.ZombieIncident, bool>>(
                                Expression.Call(Expression.Property(paramStart,
                                    typeof(dtos.ZombieIncident).GetProperty(propertyName).GetGetMethod()),
                                    typeof(String).GetMethod(searchType.ToString(), new Type[] { typeof(String) }),
                                    new Expression[] { Expression.Constant(searchValue, typeof(string)) }),
                        new ParameterExpression[] { paramStart });

                    foreach (dtos.ZombieIncident zombie in zombieRepository.FilterBy(searchExp).ToList())
                    {
                        zombies.Add(ZombieIncidentDTOMapper.FromDTO(zombie));
                    }
                }
            }
            return new SearchZombieIncidentsResponse(zombies);
        }
        catch (BusinessLogicException bex)
        {
            throw;
        }
        catch (Exception ex)
        {
            WcfExemplar.Common.Logging.LogManager.Instance.Logger("Tasks").ErrorFormat("{0}\r\n{1}",ex.Message, ex.StackTrace);
            throw;
        }
    }
}

As before we simply make use of the things we have already seen

  1. Business logic: To ensure the new zombie search is valid (i.e., has all required values)
  2. DTO: Transfer objects to write to the database
  3. Unit of work: Allows work to be done one transaction
  4. Repository: Allows us to abstract away the operations with the database, we simply use the repository

One thing to note is that if the search type is "ShowAll" we simply return all existing incidents, otherwise we construct a dynamic LambdaExpression from the Tasks input values, which is used against the Repository, which will query the database correctly.

That's It

That is all I wanted to say this time. I also think this may be my last WPF article for a while as I want to spend some time getting my JavaScript skills up to date, so I will be spending some time on things like

  • Node.js
  • Backbone.js
  • Underscore.js
  • D3.js
  • Raphael.js
  • Easle.js

To name but a few, so you can hope to see some articles around those once I begin/become terrified/beg for help or possibly even master them.

Special Thanks

I would like to thank the following people

History

  • 10/10/12: Initial article
  • 17/10/12:
    • Added optimistic concurrency Fluent Nhibernate mapping stuff.
    • Added better Request/Task mapping concept. Thanks to The .NET Junkie reader, for his excellent suggestions

License

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

Share

About the Author

Sacha Barber
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)
 
- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence
 
Both of these at Sussex University UK.
 
Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
GeneralRe: Add a IHandleRequest interface PinmvpSacha Barber12-Oct-12 1:40 
GeneralRe: Add a IHandleRequest interface PinmemberThe .NET Junkie14-Oct-12 22:38 
GeneralRe: Add a IHandleRequest interface PinmvpSacha Barber15-Oct-12 12:25 
GeneralRe: Add a IHandleRequest interface Pinmembermanwood16-Oct-12 4:02 
GeneralRe: Add a IHandleRequest interface PinmvpSacha Barber16-Oct-12 4:34 
GeneralRe: Add a IHandleRequest interface PinmvpSacha Barber17-Oct-12 4:59 
GeneralMy vote of 5 Pinmemberxr1fab10-Oct-12 20:36 
GeneralRe: My vote of 5 PinmvpSacha Barber10-Oct-12 21:56 
Thanks man
Sacha Barber
  • Microsoft Visual C# MVP 2008-2012
  • Codeproject MVP 2008-2012
Open Source Projects
Cinch SL/WPF MVVM

Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralMy vote of 5 PinmemberJ. Wijaya10-Oct-12 15:53 
GeneralRe: My vote of 5 PinmvpSacha Barber10-Oct-12 19:19 
GeneralMy vote of 5 PinmemberFlorian Rappl10-Oct-12 10:53 
GeneralRe: My vote of 5 PinmvpSacha Barber10-Oct-12 11:00 
GeneralMy vote of 5 Pinmemberrmx9910-Oct-12 10:06 
GeneralRe: My vote of 5 PinmvpSacha Barber10-Oct-12 10:10 
GeneralMy vote of 5 PinmentorDaveAuld10-Oct-12 9:59 
GeneralRe: My vote of 5 PinmvpSacha Barber10-Oct-12 10:09 

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

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

| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 15 Apr 2013
Article Copyright 2012 by Sacha Barber
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid