Service Locator Pattern In C#

Standard

Have you ever use ASP.NET membership provider? All ASP.NET developers know what is the membership and what is the membership provider. They can easily use one of the providers shipped with .NET framework, they can use a provider shipped with a certain product or they can build their own provider. The behind scene for this functionality is a design pattern called Service Locator. Service Locator Design Pattern is a very useful design pattern to make your software extensible and maintainable. Actually  there are many benefits push you to use this approach especially on the big systems as you can separate any part (Service) of your system and test it, re-implement it and deploy it without affect other running parts of the system because of the loosely-coupled relationship between Application and Service Implementation. We can summarize some its benefits as following:

  1. Perform unit-test in a easier way even on the production servers
  2. Make your Application extensible by a third parties
  3. Maintain your Application in a easier way
  4. Provide your customer by updates and hot-fixes that can be deployed without a complete new build

Two main parts are the keys to understand the Service Locator pattern:

  1. Define the interface to be used to consume the service
  2. Implement the host application that locate and use the service
  3. Implement this interface and register your type to be used as a service

In this post we will walk-through  to implement Calculator that any one can extend its functionality to support new mathematical operations

Source code for this post is available at https://pocs.codeplex.com

1. Define Your Interface

In the following steps we will define our service interface which is considered to be used as a contract between the service locator object and the implementation of the service. We will name it CalculatorOperationAPI as I supposed the result DLL will be ship with the application to be used to extend the functionality

  1. Open Visual Studio and Add new Class Library Project named CalculatorOperationAPI
  2. Add new interface item name it IOperation
  3. Add Calculate and GetOperationParamters methods as following
using System;
using System.Collections.Generic;

namespace CalculatorOperationAPI
{
    public interface IOperation
    {
        /// <summary>
        /// Return a list of the required paramters for current operation
        /// </summary>
        List<string> GetOperaionParameters();

        /// <summary>
        /// Calculate method receive a dictionary with all parameter names and values
        /// Make the required operation and return the result as string
        /// </summary>
        string Calculate(Dictionary<string, string> parameters);
    }
}

2. Build Host Application (Service Locator)

In this section we will write our calculator application code which represent our service locator mechanism that use the previous interface in all operations.

Note that:

  1. Our application doesn’t know about the implementations of the different operations which it will provide to the user
  2. The application just reads all the registered operations and list them to user to be used
  3. The registration of operations are saved in static list object inside the code. However in a real case you have to read it from configuration file or database

Now follow the following steps to build service locator mechanism:

  1. Add new Console Applicaton project and name it Calculator
  2. Change the program.cs as following
using CalculatorOperationAPI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting;
using System.Text;

namespace Calculator
{
    class Program
    {
        static List<ServiceDetails> registeredOperations = new List<ServiceDetails>();

        static void Main(string[] args)
        {
            //1- Register operations to the used by the application
            registeredOperations.Add(new ServiceDetails() {DisplayName = "Add", AssemblyName = "Operations", TypeName = "Operations.Add" });
            registeredOperations.Add(new ServiceDetails() { DisplayName = "Subtract", AssemblyName = "Operations", TypeName = "Operations.Subtract" });


            //2- Display all the registerd operation to the user to select the required operation 
            for(int i = 0; i < registeredOperations.Count(); i ++)
            {
                ServiceDetails service = registeredOperations[i];
                Console.WriteLine(string.Format("{0} : {1}",i,service.DisplayName));
            }
            Console.WriteLine("");
            Console.Write("Select Operation Number : ");
            int OperationNumber;
            string operation = Console.ReadLine();
            Console.WriteLine("");

            //3- Get the details of the selected operation from the registered services list
            ServiceDetails operationDetails = null;
            if (int.TryParse(operation, out OperationNumber) && OperationNumber <= registeredOperations.Count())
                operationDetails = registeredOperations[OperationNumber];

            if (operationDetails == null)
            {
                //if the user enter a wrong operation number (Not listed operation)
                Console.WriteLine("Operation Not Found");
            }
            else
            {
                //4- Use the registered operation to calculate the value by initiate a new object on the runtime
                ObjectHandle obj = Activator.CreateInstance(operationDetails.AssemblyName, operationDetails.TypeName);

                //5- Cast this object to the IOperation interface
                IOperation operationImplementation = (obj.Unwrap()) as IOperation;

                //6- Use the interface to get list of required paramters for this application
                Dictionary<string, string> parameters = new Dictionary<string, string>();
                
                //7- Ask the user to enter the paramters value
                List<string> listOfParamters = operationImplementation.GetOperaionParameters();
                foreach (var paramName in listOfParamters)
                {
                    Console.Write(string.Format("{0} : ",paramName));
                    parameters.Add(paramName, Console.ReadLine());
                }

                //8- Use the interface to calculate the result and display it to the user
                Console.WriteLine("");
                Console.WriteLine(string.Format("The result is : {0}",operationImplementation.Calculate(parameters)));
            }

            Console.WriteLine("Press any key to exit....");
            Console.ReadKey();
        }
    }

    class ServiceDetails
    {
        public string DisplayName { get; set; }
        public string Assembly { get; set; }
        public string TypeName { get; set; }
    }
}

On the previous code we fist define a class called ServiceDetails that used to store one operation then we defined a global List<ServiceDetails> variable and named it  registedOperations that will store all the registered operations on our application. In the main method we make the following steps that metioned on the code

1. Initialized the registeredOperations list with all operations 

In this step you register all different operations developed as  a separated parts as we will see in the coming section. Registeration is made by add new operation (Service) and give it a name and define its assembly name and type name

2. Locate the operaton (Service) selected by the user 

In this step on our application we request the user to select one of the registered services by its display name. After the user select the service number we retrieve the operation (Service) data from registeredOperations list

3. Consume the Operation (Service)

In this step the application create a new instance from the operation type and cast it to IOperation interface then query its methods to calculate the operation and display the result to the user

3. Define Operations That Shipped With Your Application

Any one who have the CalculatorOperationAPI assembly can use it to implement IOpeartion interface as following:

using CalculatorOperationAPI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Operations
{
    public class Add:IOperation
    {
        public string Calculate(Dictionary<string, string> parameters)
        {
            int firstNumber = int.Parse(parameters["First Int Number"]);
            int secondNumber = int.Parse(parameters["Second Int Number"]);
            int result = firstNumber + secondNumber;

            return result.ToString();
        }

        public List<string> GetOperaionParameters()
        {
            List<string> paramters = new List<string>();

            paramters.Add("First Int Number");
            paramters.Add("Second Int Number");

            return paramters;
        }
    }
}

After you finish the interface implementation all you need are:

  1. Register the new type which is implement the interface it to Calculator application
  2. Copy the assembly to bin folder next to our application exe file to be accessible by the Calculator application
On this prove of concept we add a reference to the Operations.dll to simplify the copy process

Here are the result:

Service Locator Pattern In C#Service Locator Pattern In C#Service Locator Pattern In C#

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s