Service locator design pattern is a pattern that is used to remove direct dependency of service client on service provider.
Main idea here is building a centralized repository of services then client will get required service from repository this way we will decouple service implementation & Service clients.
To demonstrate this pattern we will create a console application say (MyConsoleApp).
Then add a class (say MyEntity.cs).
Below is my code from MyEntity.cs.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace MyConsoleApp
{
//helper class
public class Utils
{
public static string RandomString(int maxSize)
{
char[] chars = new char[62];
chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
byte[] data = new byte[1];
using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
{
crypto.GetNonZeroBytes(data);
data = new byte[maxSize];
crypto.GetNonZeroBytes(data);
}
StringBuilder result = new StringBuilder(maxSize);
foreach (byte b in data)
{
result.Append(chars[b % (chars.Length)]);
}
return result.ToString();
}
}
//interface
public interface IAddIntService
{
int Add(int param1,int param2);
string ServiceInstanceId
{
get;
set;
}
DateTime InstatiateTime
{
get;
set;
}
}
public interface IAddLongService
{
long Add(long param1,long param2);
string ServiceInstanceId
{
get;
set;
}
DateTime InstatiateTime
{
get;
set;
}
}
public interface IAddDoubleService
{
double Add(double param1, double param2);
string ServiceInstanceId
{
get;
set;
}
DateTime InstatiateTime
{
get;
set;
}
}
//implementation
class AddInt : IAddIntService
{
//constructor
public AddInt()
{
this.ServiceInstanceId = Utils.RandomString(15);
this.InstatiateTime = DateTime.Now;
}
public string ServiceInstanceId
{
get;
set;
}
public DateTime InstatiateTime
{
get;
set;
}
public int Add(int param1, int param2)
{
return Convert.ToInt32(param1 + param2);
}
}
class AddLong : IAddLongService
{
public AddLong()
{
this.ServiceInstanceId = Utils.RandomString(15);
this.InstatiateTime = DateTime.Now;
}
public DateTime InstatiateTime
{
get;
set;
}
public long Add(long param1, long param2)
{
return Convert.ToInt64(param1 + param2);
}
public string ServiceInstanceId
{
get;
set;
}
}
class AddDouble : IAddDoubleService
{
public AddDouble()
{
this.ServiceInstanceId = Utils.RandomString(15);
this.InstatiateTime = DateTime.Now;
}
public DateTime InstatiateTime
{
get;
set;
}
public double Add(double param1, double param2)
{
return Convert.ToDouble(param1 + param2);
}
public string ServiceInstanceId
{
get;
set;
}
}
}
Here I am creating three interfaces
1) IAddIntService:It has method to implement
int Add(int param1,int param2);
2) IAddLongService:It has method to implement
long Add(long param1,long param2);
3) IAddDoubleService :It has method to implement
double Add(double param1, double param2);
Each interface has two additional properties
a) InstatiateTime: Holds time at which object created
b) ServiceInstanceId: Holds instance id i.e. random 15 digited string to differentiate instance created approximately at same time.
Interfaces above are implemented as follows
1)AddInt class implements IAddIntService
2)AddLong class implements IAddLongService
3)AddDouble class implements IAddDoubleService
At the time of instantiation of these three class objects in respective constructor I am initializing properties ServiceInstanceId & InstatiateTime.
Now Let’s add one more class file say “MyPattern.cs”
Below is code in MyPattern.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace MyConsoleApp
{
public interface IServiceLocator
{
T GetService<T>();
DateTime InstatiateTime
{
get;
set;
}
string ServiceInstanceId
{
get;
set;
}
}
//service instance eager loaded
class ServiceLocator : IServiceLocator
{
//repository to maintain list of available services
private IDictionary<object, object> services;
//constructor
internal ServiceLocator()
{
this.InstatiateTime = DateTime.Now;
this.ServiceInstanceId = Utils.RandomString(15);
services = new Dictionary<object, object>();
//list of available services
this.services.Add(typeof(IAddIntService), new AddInt());
this.services.Add(typeof(IAddLongService), new AddLong());
this.services.Add(typeof(IAddDoubleService), new AddDouble());
}
public T GetService<T>()
{
try
{
return (T)services[typeof(T)];
}
catch (KeyNotFoundException)
{
throw new ApplicationException("The requested service is not registered");
}
}
//property implemented
public DateTime InstatiateTime
{
get;
set;
}
public string ServiceInstanceId
{
get;
set;
}
}
//service instance lazy loaded
internal class LazyServiceLocator : IServiceLocator
{
//repository that keeps maps of service type & interface that it implements so that when required we can instantiate object of desired type
private IDictionary<Type, Type> services;
//repository that keeps maps of service type object instatiated & interface that it implements so when available no need to reinstantiate
private IDictionary<Type, object> instantiatedServices;
//constructor
internal LazyServiceLocator()
{
this.InstatiateTime = DateTime.Now;
this.ServiceInstanceId = Utils.RandomString(15);
//initialize repositories
this.services = new Dictionary<Type, Type>();
this.instantiatedServices = new Dictionary<Type, object>();
this.BuildServiceTypesMap();
}
private void BuildServiceTypesMap()
{
this.services.Add(typeof(IAddIntService), typeof(AddInt));
this.services.Add(typeof(IAddLongService), typeof(AddLong));
this.services.Add(typeof(IAddDoubleService), typeof(AddDouble));
}
public T GetService<T>()
{
if (this.instantiatedServices.ContainsKey(typeof(T)))
{
//found in earlier instantiated objects
return (T)this.instantiatedServices[typeof(T)];
}
else
{
// lazy initialization
try
{
// use reflection to invoke the service
ConstructorInfo constructor = services[typeof(T)].GetConstructor(new Type[0]);
T service = (T)constructor.Invoke(null);
// add the service to the ones that we have already instantiated
instantiatedServices.Add(typeof(T), service);
return service;
}
catch (KeyNotFoundException)
{
throw new ApplicationException("The requested service is not registered");
}
}
}
//property implemented
public DateTime InstatiateTime
{
get;
set;
}
public string ServiceInstanceId
{
get;
set;
}
}
//service locator SingleTon
internal class LazySingleTonServiceLocator : IServiceLocator
{
//repository that keeps maps of service type & interface that it implements so that when required we can instantiate object of desired type
private IDictionary<Type, Type> services;
private static readonly object TheLock = new Object();
//stores the instance of service locator that will be instantiated only once then reused
private static IServiceLocator ServiceLocatorInstance;
//repository that keeps maps of service type object instatiated & interface that it implements so when available no need to reinstantiate
private readonly IDictionary<Type, object> instantiatedServices;
//private constructor so service locator instance can not be created by others than GetServiceLocatorInstance method
private LazySingleTonServiceLocator()
{
this.InstatiateTime = DateTime.Now;
this.ServiceInstanceId = Utils.RandomString(15);
//initialize repositories
this.services = new Dictionary<Type, Type>();
this.instantiatedServices = new Dictionary<Type, object>();
this.BuildServiceTypesMap();
}
private void BuildServiceTypesMap()
{
this.services.Add(typeof(IAddIntService), typeof(AddInt));
this.services.Add(typeof(IAddLongService), typeof(AddLong));
this.services.Add(typeof(IAddDoubleService), typeof(AddDouble));
}
//static method to retreive service locator instance
public static IServiceLocator GetServiceLocatorInstance
{
get
{
lock (TheLock) // thread safety
{
if (ServiceLocatorInstance == null)
{
ServiceLocatorInstance = new ServiceLocator();//private constructor accessible from same class only
}
}
return ServiceLocatorInstance;
}
}
//property implemented
public DateTime InstatiateTime
{
get;
set;
}
public string ServiceInstanceId
{
get;
set;
}
public T GetService<T>()
{
if (this.instantiatedServices.ContainsKey(typeof(T)))
{
//found in earlier instantiated objects
return (T)this.instantiatedServices[typeof(T)];
}
else
{
// lazy initialization
try
{
// use reflection to invoke the service
ConstructorInfo constructor = services[typeof(T)].GetConstructor(new Type[0]);
T service = (T)constructor.Invoke(null);
// add the service to the ones that we have already instantiated
instantiatedServices.Add(typeof(T), service);
return service;
}
catch (KeyNotFoundException)
{
throw new ApplicationException("The requested service is not registered");
}
}
}
}
}
In this code I am creating IServiceLocator interface which has generic method
T GetService<T>();
Further I am adding to properties on the line of our entity interfaces.
1) InstatiateTime
2) ServiceInstanceId
That will save time at which instance of class implementing IServiceLocator created at and its instance Id.
There are three different implementation of interface IServiceLocator
1) ServiceLocator: Here constructor create repository of available services method GetService provide available service. Drawback in this implementation is object of services created forehand even before somebody needs it.
2) LazyServiceLocator: Here also constructor create repository of available service type & their interface type. When First request for certain service come it creates the object of service and cache that object in on more repository which keeps map between service type & its object. Subsequent request to same service are serviced through cached object.This implementation takes care of drawback in earlier implementation i.e. We are creating object of service whenever actual object is needed i.e. Lazy implementation.
Again what happens is if we end up with creating multiple instance of LazyServiceLocator class which but one instance is suffice to handle multiple call.
3) LazySingleTonServiceLocator: Here we improve our design further by making constructor of LazySingleTonServiceLocator private & adding a public static method to get required instance
public static IServiceLocator GetServiceLocatorInstance
Inside this method calling private constructor two create instance of LazySingleTonServiceLocator and caching it in a static variable, on subsequent request we will not create a new instance of LazySingleTonServiceLocator object but provide one that is already created.Locking is used so that multiple thread do not enter in object instantiation logic and creating multiple instance of LazySingleTonServiceLocator defeating purpose of making it singleton.
Inside program.cs add following code ,it demonstrate use of Service Locator classes
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace MyConsoleApp
{
class Program
{
static void Main(string[] args)
{
#region NonLazyLocator
Console.WriteLine(" ############### Simple Service Locator ###############");
IServiceLocator Locator = new ServiceLocator();
Console.WriteLine("\n Simple Service Locator Object created \n\n Instance Id:{0}:{0} \n Instatiated At {1}", Locator.ServiceInstanceId, Locator.InstatiateTime.ToLongTimeString());
//sleep
Console.WriteLine("\n Sleeping for moments for 20 secs...\n");
System.Threading.Thread.Sleep(20000);
//asking for service to add int
//int
Console.WriteLine("\n Looking for compatible service to add int...\n");
IAddIntService LocatedService = Locator.GetService<IAddIntService>();
int intOp = LocatedService.Add(25, 57);
Console.WriteLine(" Service Instance to add int found...\n");
Console.WriteLine(" Service Instance Id:{0}:{0} \n Instatiated At {1}", LocatedService.ServiceInstanceId, LocatedService.InstatiateTime.ToLongTimeString());
//long
Console.WriteLine("\n Looking for compatible service to add long...\n");
IAddLongService LocatedServiceLong = Locator.GetService<IAddLongService>();
long intOpLong = LocatedServiceLong.Add(25, 57);
Console.WriteLine(" Service Instance to add long found...\n");
Console.WriteLine(" Service Instance Id:{0}:{0} \n Instatiated At {1}", LocatedServiceLong.ServiceInstanceId, LocatedServiceLong.InstatiateTime.ToLongTimeString());
//int again
Console.WriteLine("\n Looking for compatible service to add int again...\n");
IAddIntService LocatedServiceAgain = Locator.GetService<IAddIntService>();
int intOpAgain = LocatedServiceAgain.Add(56, 35);
Console.WriteLine(" Service Instance to add int found...\n");
Console.WriteLine(" Service Instance Id:{0}:{0} \n Instatiated At {1}", LocatedServiceAgain.ServiceInstanceId, LocatedServiceAgain.InstatiateTime.ToLongTimeString());
Console.WriteLine("\n Conclusion:Service Locator & Service Instances created at same time\n");
Console.WriteLine(" ####################################################\n\n\n");
#endregion
#region LazyLocator
Console.WriteLine("\n ############### Lazy Service Locator ###############");
IServiceLocator LazyLocator = new LazyServiceLocator();
Console.WriteLine("\n Lazy Service Locator Object created \n\n Instance Id:{0} \n Instatiated At :{1}\n", LazyLocator.ServiceInstanceId, LazyLocator.InstatiateTime.ToLongTimeString());
//sleep
Console.WriteLine("\n Sleeping for moments for 20 secs...\n");
System.Threading.Thread.Sleep(20000);
//asking for service to add int
IAddIntService LocatedLazyIntService = LazyLocator.GetService<IAddIntService>();
int intOpLazy = LocatedLazyIntService.Add(25, 57);
Console.WriteLine("\n Looking for compatible service to add int ...\n");
Console.WriteLine(" Service Instance to add int found...\n");
Console.WriteLine(" Service Instance Id:{0} \n Instatiated At: {1}\n", LocatedLazyIntService.ServiceInstanceId, LocatedLazyIntService.InstatiateTime.ToLongTimeString());
//asking for service to add long
IAddLongService LocatedLazyLongService = LazyLocator.GetService<IAddLongService>();
long longOpLazy = LocatedLazyLongService.Add(255, 457);
Console.WriteLine("\n Looking for compatible service to long int ...\n");
Console.WriteLine(" Service Instance to add long found...\n");
Console.WriteLine(" Service Instance Id:{0} \n Instatiated At: {1}\n", LocatedLazyLongService.ServiceInstanceId, LocatedLazyLongService.InstatiateTime.ToLongTimeString());
//asking again for service to add int again
IAddIntService LocatedLazyIntServiceAgain = LazyLocator.GetService<IAddIntService>();
int intOpLazyAgain = LocatedLazyIntServiceAgain.Add(253, 567);
Console.WriteLine("\n Looking for compatible service to add int again...\n");
Console.WriteLine(" Service Instance to add int found...\n");
Console.WriteLine(" Service Instance Id:{0} \n Instatiated At: {1}\n", LocatedLazyIntServiceAgain.ServiceInstanceId, LocatedLazyIntServiceAgain.InstatiateTime.ToLongTimeString());
Console.WriteLine("\n Conclusion:Service Locator & Service Instances created at different time (lazy loading...)\n");
Console.WriteLine(" ####################################################");
#endregion
#region LazySingleTonLocator
Console.WriteLine("\n\n\n ############### Lazy SingleTon Service Locator ###############");
//pulling service locator object
IServiceLocator LazySingleTonLocator = LazySingleTonServiceLocator.GetServiceLocatorInstance;
Console.WriteLine("\n Lazy Singletome Service Locator Object created (once as SingleTon) \n\n Instance Id:{0} \n Instatiated At :{1}\n", LazySingleTonLocator.ServiceInstanceId, LazySingleTonLocator.InstatiateTime.ToLongTimeString());
IAddIntService LocatedLazySingleTonIntService = LazySingleTonLocator.GetService<IAddIntService>();
int intOpLazySingleTon = LocatedLazySingleTonIntService.Add(25, 57);
//sleep
Console.WriteLine("\n Sleeping for moments for 20 secs...\n");
System.Threading.Thread.Sleep(20000);
//again pulling Service Locator object
IServiceLocator LazySingleTonLocatorAgain = LazySingleTonServiceLocator.GetServiceLocatorInstance;
Console.WriteLine("\n Lazy Singletome Service Locator pulled again \n\n Instance Id:{0} \n Instatiated At :{1}\n", LazySingleTonLocator.ServiceInstanceId, LazySingleTonLocator.InstatiateTime.ToLongTimeString());
Console.WriteLine("\n Conclusion:Lazy Singletome Service Locator instance is not created multiple time (SingleTon)");
Console.WriteLine(" ####################################################");
#endregion
Console.ReadKey();
}
}
}
How to call our 1st Implementation?
IServiceLocator Locator = new ServiceLocator();
IAddIntService LocatedService = Locator.GetService<IAddIntService>();
int intOp = LocatedService.Add(25, 57);
How to call our 2nd Implementation?
IServiceLocator LazyLocator = new LazyServiceLocator();
IAddIntService LocatedLazyIntService = LazyLocator.GetService<IAddIntService>();
int intOpLazy = LocatedLazyIntService.Add(25, 57);
How to call our 3rd Implementation?
IServiceLocator LazySingleTonLocator = LazySingleTonServiceLocator.GetServiceLocatorInstance;
IAddIntService LocatedLazySingleTonIntService = LazySingleTonLocator.GetService<IAddIntService>();
int intOpLazySingleTon = LocatedLazySingleTonIntService.Add(25, 57);
Compile you console application and run, Here is my output
Output:
############### Simple Service Locator ###############
Simple Service Locator Object created
Instance Id:U1JGBWKB4deh5fj:U1JGBWKB4deh5fj
Instatiated At 2:36:22 PM
Sleeping for moments for 20 secs...
Looking for compatible service to add int...
Service Instance to add int found...
Service Instance Id:Jr0gzKODGNs4K0n:Jr0gzKODGNs4K0n
Instatiated At 2:36:22 PM
Looking for compatible service to add long...
Service Instance to add long found...
Service Instance Id:d37g9tIkCQcmZvG:d37g9tIkCQcmZvG
Instatiated At 2:36:22 PM
Looking for compatible service to add int again...
Service Instance to add int found...
Service Instance Id:Jr0gzKODGNs4K0n:Jr0gzKODGNs4K0n
Instatiated At 2:36:22 PM
Conclusion:Service Locator & Service Instances created at same time #####################################################################
############### Lazy Service Locator ###############################
Lazy Service Locator Object created
Instance Id:QwADmquGl9p4JBm
Instatiated At :2:36:42 PM
Sleeping for moments for 20 secs...
Looking for compatible service to add int ...
Service Instance to add int found...
Service Instance Id:oI7FJx3mZWmCdAc
Instatiated At: 2:37:02 PM
Looking for compatible service to long int ...
Service Instance to add long found...
Service Instance Id:UUz7Qkv7XjmvMOb
Instatiated At: 2:37:02 PM
Looking for compatible service to add int again...
Service Instance to add int found...
Service Instance Id:oI7FJx3mZWmCdAc
Instatiated At: 2:37:02 PM
Conclusion:Service Locator & Service Instances created at different time (lazy loading...)
####################################################################
############### Lazy SingleTon Service Locator ####################
Lazy Singletome Service Locator Object created (once as SingleTon)
Instance Id:5pPqsOZB3NReL44
Instatiated At :2:37:02 PM
Sleeping for moments for 20 secs...
Lazy Singletome Service Locator pulled again
Instance Id:5pPqsOZB3NReL44
Instatiated At :2:37:02 PM
Conclusion:Lazy Singletome Service Locator instance is not created multiple time (SingleTon)
####################################################
In console I am printing instance time & Instance Id of service locator object & service object whenever required to confirm that service instance are created lazily & service locator is not creating multiple time enforcing SingleTon implementation.
My code is adaptation of Stephan (see reference) but with service class that I can connect more easily.
References:
http://stefanoricciardi.com/2009/09/25/service-locator-pattern-in-csharpa-simple-example/
Main idea here is building a centralized repository of services then client will get required service from repository this way we will decouple service implementation & Service clients.
To demonstrate this pattern we will create a console application say (MyConsoleApp).
Then add a class (say MyEntity.cs).
Below is my code from MyEntity.cs.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace MyConsoleApp
{
//helper class
public class Utils
{
public static string RandomString(int maxSize)
{
char[] chars = new char[62];
chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
byte[] data = new byte[1];
using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
{
crypto.GetNonZeroBytes(data);
data = new byte[maxSize];
crypto.GetNonZeroBytes(data);
}
StringBuilder result = new StringBuilder(maxSize);
foreach (byte b in data)
{
result.Append(chars[b % (chars.Length)]);
}
return result.ToString();
}
}
//interface
public interface IAddIntService
{
int Add(int param1,int param2);
string ServiceInstanceId
{
get;
set;
}
DateTime InstatiateTime
{
get;
set;
}
}
public interface IAddLongService
{
long Add(long param1,long param2);
string ServiceInstanceId
{
get;
set;
}
DateTime InstatiateTime
{
get;
set;
}
}
public interface IAddDoubleService
{
double Add(double param1, double param2);
string ServiceInstanceId
{
get;
set;
}
DateTime InstatiateTime
{
get;
set;
}
}
//implementation
class AddInt : IAddIntService
{
//constructor
public AddInt()
{
this.ServiceInstanceId = Utils.RandomString(15);
this.InstatiateTime = DateTime.Now;
}
public string ServiceInstanceId
{
get;
set;
}
public DateTime InstatiateTime
{
get;
set;
}
public int Add(int param1, int param2)
{
return Convert.ToInt32(param1 + param2);
}
}
class AddLong : IAddLongService
{
public AddLong()
{
this.ServiceInstanceId = Utils.RandomString(15);
this.InstatiateTime = DateTime.Now;
}
public DateTime InstatiateTime
{
get;
set;
}
public long Add(long param1, long param2)
{
return Convert.ToInt64(param1 + param2);
}
public string ServiceInstanceId
{
get;
set;
}
}
class AddDouble : IAddDoubleService
{
public AddDouble()
{
this.ServiceInstanceId = Utils.RandomString(15);
this.InstatiateTime = DateTime.Now;
}
public DateTime InstatiateTime
{
get;
set;
}
public double Add(double param1, double param2)
{
return Convert.ToDouble(param1 + param2);
}
public string ServiceInstanceId
{
get;
set;
}
}
}
Here I am creating three interfaces
1) IAddIntService:It has method to implement
int Add(int param1,int param2);
2) IAddLongService:It has method to implement
long Add(long param1,long param2);
3) IAddDoubleService :It has method to implement
double Add(double param1, double param2);
Each interface has two additional properties
a) InstatiateTime: Holds time at which object created
b) ServiceInstanceId: Holds instance id i.e. random 15 digited string to differentiate instance created approximately at same time.
Interfaces above are implemented as follows
1)AddInt class implements IAddIntService
2)AddLong class implements IAddLongService
3)AddDouble class implements IAddDoubleService
At the time of instantiation of these three class objects in respective constructor I am initializing properties ServiceInstanceId & InstatiateTime.
Now Let’s add one more class file say “MyPattern.cs”
Below is code in MyPattern.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace MyConsoleApp
{
public interface IServiceLocator
{
T GetService<T>();
DateTime InstatiateTime
{
get;
set;
}
string ServiceInstanceId
{
get;
set;
}
}
//service instance eager loaded
class ServiceLocator : IServiceLocator
{
//repository to maintain list of available services
private IDictionary<object, object> services;
//constructor
internal ServiceLocator()
{
this.InstatiateTime = DateTime.Now;
this.ServiceInstanceId = Utils.RandomString(15);
services = new Dictionary<object, object>();
//list of available services
this.services.Add(typeof(IAddIntService), new AddInt());
this.services.Add(typeof(IAddLongService), new AddLong());
this.services.Add(typeof(IAddDoubleService), new AddDouble());
}
public T GetService<T>()
{
try
{
return (T)services[typeof(T)];
}
catch (KeyNotFoundException)
{
throw new ApplicationException("The requested service is not registered");
}
}
//property implemented
public DateTime InstatiateTime
{
get;
set;
}
public string ServiceInstanceId
{
get;
set;
}
}
//service instance lazy loaded
internal class LazyServiceLocator : IServiceLocator
{
//repository that keeps maps of service type & interface that it implements so that when required we can instantiate object of desired type
private IDictionary<Type, Type> services;
//repository that keeps maps of service type object instatiated & interface that it implements so when available no need to reinstantiate
private IDictionary<Type, object> instantiatedServices;
//constructor
internal LazyServiceLocator()
{
this.InstatiateTime = DateTime.Now;
this.ServiceInstanceId = Utils.RandomString(15);
//initialize repositories
this.services = new Dictionary<Type, Type>();
this.instantiatedServices = new Dictionary<Type, object>();
this.BuildServiceTypesMap();
}
private void BuildServiceTypesMap()
{
this.services.Add(typeof(IAddIntService), typeof(AddInt));
this.services.Add(typeof(IAddLongService), typeof(AddLong));
this.services.Add(typeof(IAddDoubleService), typeof(AddDouble));
}
public T GetService<T>()
{
if (this.instantiatedServices.ContainsKey(typeof(T)))
{
//found in earlier instantiated objects
return (T)this.instantiatedServices[typeof(T)];
}
else
{
// lazy initialization
try
{
// use reflection to invoke the service
ConstructorInfo constructor = services[typeof(T)].GetConstructor(new Type[0]);
T service = (T)constructor.Invoke(null);
// add the service to the ones that we have already instantiated
instantiatedServices.Add(typeof(T), service);
return service;
}
catch (KeyNotFoundException)
{
throw new ApplicationException("The requested service is not registered");
}
}
}
//property implemented
public DateTime InstatiateTime
{
get;
set;
}
public string ServiceInstanceId
{
get;
set;
}
}
//service locator SingleTon
internal class LazySingleTonServiceLocator : IServiceLocator
{
//repository that keeps maps of service type & interface that it implements so that when required we can instantiate object of desired type
private IDictionary<Type, Type> services;
private static readonly object TheLock = new Object();
//stores the instance of service locator that will be instantiated only once then reused
private static IServiceLocator ServiceLocatorInstance;
//repository that keeps maps of service type object instatiated & interface that it implements so when available no need to reinstantiate
private readonly IDictionary<Type, object> instantiatedServices;
//private constructor so service locator instance can not be created by others than GetServiceLocatorInstance method
private LazySingleTonServiceLocator()
{
this.InstatiateTime = DateTime.Now;
this.ServiceInstanceId = Utils.RandomString(15);
//initialize repositories
this.services = new Dictionary<Type, Type>();
this.instantiatedServices = new Dictionary<Type, object>();
this.BuildServiceTypesMap();
}
private void BuildServiceTypesMap()
{
this.services.Add(typeof(IAddIntService), typeof(AddInt));
this.services.Add(typeof(IAddLongService), typeof(AddLong));
this.services.Add(typeof(IAddDoubleService), typeof(AddDouble));
}
//static method to retreive service locator instance
public static IServiceLocator GetServiceLocatorInstance
{
get
{
lock (TheLock) // thread safety
{
if (ServiceLocatorInstance == null)
{
ServiceLocatorInstance = new ServiceLocator();//private constructor accessible from same class only
}
}
return ServiceLocatorInstance;
}
}
//property implemented
public DateTime InstatiateTime
{
get;
set;
}
public string ServiceInstanceId
{
get;
set;
}
public T GetService<T>()
{
if (this.instantiatedServices.ContainsKey(typeof(T)))
{
//found in earlier instantiated objects
return (T)this.instantiatedServices[typeof(T)];
}
else
{
// lazy initialization
try
{
// use reflection to invoke the service
ConstructorInfo constructor = services[typeof(T)].GetConstructor(new Type[0]);
T service = (T)constructor.Invoke(null);
// add the service to the ones that we have already instantiated
instantiatedServices.Add(typeof(T), service);
return service;
}
catch (KeyNotFoundException)
{
throw new ApplicationException("The requested service is not registered");
}
}
}
}
}
In this code I am creating IServiceLocator interface which has generic method
T GetService<T>();
Further I am adding to properties on the line of our entity interfaces.
1) InstatiateTime
2) ServiceInstanceId
That will save time at which instance of class implementing IServiceLocator created at and its instance Id.
There are three different implementation of interface IServiceLocator
1) ServiceLocator: Here constructor create repository of available services method GetService provide available service. Drawback in this implementation is object of services created forehand even before somebody needs it.
2) LazyServiceLocator: Here also constructor create repository of available service type & their interface type. When First request for certain service come it creates the object of service and cache that object in on more repository which keeps map between service type & its object. Subsequent request to same service are serviced through cached object.This implementation takes care of drawback in earlier implementation i.e. We are creating object of service whenever actual object is needed i.e. Lazy implementation.
Again what happens is if we end up with creating multiple instance of LazyServiceLocator class which but one instance is suffice to handle multiple call.
3) LazySingleTonServiceLocator: Here we improve our design further by making constructor of LazySingleTonServiceLocator private & adding a public static method to get required instance
public static IServiceLocator GetServiceLocatorInstance
Inside this method calling private constructor two create instance of LazySingleTonServiceLocator and caching it in a static variable, on subsequent request we will not create a new instance of LazySingleTonServiceLocator object but provide one that is already created.Locking is used so that multiple thread do not enter in object instantiation logic and creating multiple instance of LazySingleTonServiceLocator defeating purpose of making it singleton.
Inside program.cs add following code ,it demonstrate use of Service Locator classes
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace MyConsoleApp
{
class Program
{
static void Main(string[] args)
{
#region NonLazyLocator
Console.WriteLine(" ############### Simple Service Locator ###############");
IServiceLocator Locator = new ServiceLocator();
Console.WriteLine("\n Simple Service Locator Object created \n\n Instance Id:{0}:{0} \n Instatiated At {1}", Locator.ServiceInstanceId, Locator.InstatiateTime.ToLongTimeString());
//sleep
Console.WriteLine("\n Sleeping for moments for 20 secs...\n");
System.Threading.Thread.Sleep(20000);
//asking for service to add int
//int
Console.WriteLine("\n Looking for compatible service to add int...\n");
IAddIntService LocatedService = Locator.GetService<IAddIntService>();
int intOp = LocatedService.Add(25, 57);
Console.WriteLine(" Service Instance to add int found...\n");
Console.WriteLine(" Service Instance Id:{0}:{0} \n Instatiated At {1}", LocatedService.ServiceInstanceId, LocatedService.InstatiateTime.ToLongTimeString());
//long
Console.WriteLine("\n Looking for compatible service to add long...\n");
IAddLongService LocatedServiceLong = Locator.GetService<IAddLongService>();
long intOpLong = LocatedServiceLong.Add(25, 57);
Console.WriteLine(" Service Instance to add long found...\n");
Console.WriteLine(" Service Instance Id:{0}:{0} \n Instatiated At {1}", LocatedServiceLong.ServiceInstanceId, LocatedServiceLong.InstatiateTime.ToLongTimeString());
//int again
Console.WriteLine("\n Looking for compatible service to add int again...\n");
IAddIntService LocatedServiceAgain = Locator.GetService<IAddIntService>();
int intOpAgain = LocatedServiceAgain.Add(56, 35);
Console.WriteLine(" Service Instance to add int found...\n");
Console.WriteLine(" Service Instance Id:{0}:{0} \n Instatiated At {1}", LocatedServiceAgain.ServiceInstanceId, LocatedServiceAgain.InstatiateTime.ToLongTimeString());
Console.WriteLine("\n Conclusion:Service Locator & Service Instances created at same time\n");
Console.WriteLine(" ####################################################\n\n\n");
#endregion
#region LazyLocator
Console.WriteLine("\n ############### Lazy Service Locator ###############");
IServiceLocator LazyLocator = new LazyServiceLocator();
Console.WriteLine("\n Lazy Service Locator Object created \n\n Instance Id:{0} \n Instatiated At :{1}\n", LazyLocator.ServiceInstanceId, LazyLocator.InstatiateTime.ToLongTimeString());
//sleep
Console.WriteLine("\n Sleeping for moments for 20 secs...\n");
System.Threading.Thread.Sleep(20000);
//asking for service to add int
IAddIntService LocatedLazyIntService = LazyLocator.GetService<IAddIntService>();
int intOpLazy = LocatedLazyIntService.Add(25, 57);
Console.WriteLine("\n Looking for compatible service to add int ...\n");
Console.WriteLine(" Service Instance to add int found...\n");
Console.WriteLine(" Service Instance Id:{0} \n Instatiated At: {1}\n", LocatedLazyIntService.ServiceInstanceId, LocatedLazyIntService.InstatiateTime.ToLongTimeString());
//asking for service to add long
IAddLongService LocatedLazyLongService = LazyLocator.GetService<IAddLongService>();
long longOpLazy = LocatedLazyLongService.Add(255, 457);
Console.WriteLine("\n Looking for compatible service to long int ...\n");
Console.WriteLine(" Service Instance to add long found...\n");
Console.WriteLine(" Service Instance Id:{0} \n Instatiated At: {1}\n", LocatedLazyLongService.ServiceInstanceId, LocatedLazyLongService.InstatiateTime.ToLongTimeString());
//asking again for service to add int again
IAddIntService LocatedLazyIntServiceAgain = LazyLocator.GetService<IAddIntService>();
int intOpLazyAgain = LocatedLazyIntServiceAgain.Add(253, 567);
Console.WriteLine("\n Looking for compatible service to add int again...\n");
Console.WriteLine(" Service Instance to add int found...\n");
Console.WriteLine(" Service Instance Id:{0} \n Instatiated At: {1}\n", LocatedLazyIntServiceAgain.ServiceInstanceId, LocatedLazyIntServiceAgain.InstatiateTime.ToLongTimeString());
Console.WriteLine("\n Conclusion:Service Locator & Service Instances created at different time (lazy loading...)\n");
Console.WriteLine(" ####################################################");
#endregion
#region LazySingleTonLocator
Console.WriteLine("\n\n\n ############### Lazy SingleTon Service Locator ###############");
//pulling service locator object
IServiceLocator LazySingleTonLocator = LazySingleTonServiceLocator.GetServiceLocatorInstance;
Console.WriteLine("\n Lazy Singletome Service Locator Object created (once as SingleTon) \n\n Instance Id:{0} \n Instatiated At :{1}\n", LazySingleTonLocator.ServiceInstanceId, LazySingleTonLocator.InstatiateTime.ToLongTimeString());
IAddIntService LocatedLazySingleTonIntService = LazySingleTonLocator.GetService<IAddIntService>();
int intOpLazySingleTon = LocatedLazySingleTonIntService.Add(25, 57);
//sleep
Console.WriteLine("\n Sleeping for moments for 20 secs...\n");
System.Threading.Thread.Sleep(20000);
//again pulling Service Locator object
IServiceLocator LazySingleTonLocatorAgain = LazySingleTonServiceLocator.GetServiceLocatorInstance;
Console.WriteLine("\n Lazy Singletome Service Locator pulled again \n\n Instance Id:{0} \n Instatiated At :{1}\n", LazySingleTonLocator.ServiceInstanceId, LazySingleTonLocator.InstatiateTime.ToLongTimeString());
Console.WriteLine("\n Conclusion:Lazy Singletome Service Locator instance is not created multiple time (SingleTon)");
Console.WriteLine(" ####################################################");
#endregion
Console.ReadKey();
}
}
}
How to call our 1st Implementation?
IServiceLocator Locator = new ServiceLocator();
IAddIntService LocatedService = Locator.GetService<IAddIntService>();
int intOp = LocatedService.Add(25, 57);
How to call our 2nd Implementation?
IServiceLocator LazyLocator = new LazyServiceLocator();
IAddIntService LocatedLazyIntService = LazyLocator.GetService<IAddIntService>();
int intOpLazy = LocatedLazyIntService.Add(25, 57);
How to call our 3rd Implementation?
IServiceLocator LazySingleTonLocator = LazySingleTonServiceLocator.GetServiceLocatorInstance;
IAddIntService LocatedLazySingleTonIntService = LazySingleTonLocator.GetService<IAddIntService>();
int intOpLazySingleTon = LocatedLazySingleTonIntService.Add(25, 57);
Compile you console application and run, Here is my output
Output:
############### Simple Service Locator ###############
Simple Service Locator Object created
Instance Id:U1JGBWKB4deh5fj:U1JGBWKB4deh5fj
Instatiated At 2:36:22 PM
Sleeping for moments for 20 secs...
Looking for compatible service to add int...
Service Instance to add int found...
Service Instance Id:Jr0gzKODGNs4K0n:Jr0gzKODGNs4K0n
Instatiated At 2:36:22 PM
Looking for compatible service to add long...
Service Instance to add long found...
Service Instance Id:d37g9tIkCQcmZvG:d37g9tIkCQcmZvG
Instatiated At 2:36:22 PM
Looking for compatible service to add int again...
Service Instance to add int found...
Service Instance Id:Jr0gzKODGNs4K0n:Jr0gzKODGNs4K0n
Instatiated At 2:36:22 PM
Conclusion:Service Locator & Service Instances created at same time #####################################################################
############### Lazy Service Locator ###############################
Lazy Service Locator Object created
Instance Id:QwADmquGl9p4JBm
Instatiated At :2:36:42 PM
Sleeping for moments for 20 secs...
Looking for compatible service to add int ...
Service Instance to add int found...
Service Instance Id:oI7FJx3mZWmCdAc
Instatiated At: 2:37:02 PM
Looking for compatible service to long int ...
Service Instance to add long found...
Service Instance Id:UUz7Qkv7XjmvMOb
Instatiated At: 2:37:02 PM
Looking for compatible service to add int again...
Service Instance to add int found...
Service Instance Id:oI7FJx3mZWmCdAc
Instatiated At: 2:37:02 PM
Conclusion:Service Locator & Service Instances created at different time (lazy loading...)
####################################################################
############### Lazy SingleTon Service Locator ####################
Lazy Singletome Service Locator Object created (once as SingleTon)
Instance Id:5pPqsOZB3NReL44
Instatiated At :2:37:02 PM
Sleeping for moments for 20 secs...
Lazy Singletome Service Locator pulled again
Instance Id:5pPqsOZB3NReL44
Instatiated At :2:37:02 PM
Conclusion:Lazy Singletome Service Locator instance is not created multiple time (SingleTon)
####################################################
In console I am printing instance time & Instance Id of service locator object & service object whenever required to confirm that service instance are created lazily & service locator is not creating multiple time enforcing SingleTon implementation.
My code is adaptation of Stephan (see reference) but with service class that I can connect more easily.
References:
http://stefanoricciardi.com/2009/09/25/service-locator-pattern-in-csharpa-simple-example/
No comments:
Post a Comment