Service Locator for Web Services

Oct 3, 2007 at 7:00 PM
I must admit that I do not clearly understand the concept of service locator as yet and have not been able to configure it for any web service to be consumed in a c# win app.
major doubt going thru my mind while these unsuccessfull trials this evening is as follows-: In order to consume a web service in .Net, a proxy has to be created either by 'adding a web reference' or using wsdl.exe. so a local stub has to be manually created which becomes a local class to be consumed by the application for all practical purposes. I can simply create an instace of this proxy to call web methods. Now question is why should I choose to use proxy objproxy = Service.CreateFactory<proxy> rather than proxy objproxy = new proxy()??

Sorry if this question is too basic. any guidance is appreciated. Thanks alot.
Coordinator
Oct 4, 2007 at 7:37 PM
I think you may be mixing different service concepts together. (Warning: Jargon-heavy stuff follows.) A priori, Service Locator has nothing to do with web services. In classic OO terminology, a client is any object that consumes other objects, and a server is any object that is being consumed by other objects. A service is a sort of server that has a clear role and a more or less clearly defined boundary vis à vis its clients. Such a service is a good candidate for introducing a seam into your object model, which allows you to abstract away the service, letting it become an external dependency.

However, once a service has become an abstraction (interface or abstract class), you lose the ability to create it via the new keyword, so you need some strategy to do this. Service Locator is a design pattern that adresses this need, and the Service Locator Application Block takes its name from that pattern.

You can read more about this, as well as get pointers to additional literature, in the documentation for Service Locator.

While this use of the term service differs from web services (a web service is really a Remote Façade), it doesn't preclude you from encapsulating a web service proxy in an abstraction and then use Service Locator to instantiate that encapsulation. This gives you a nice separation between your business logic and your data access (the web service, in this case). I've done so myself on several occasions, and it makes a lot of sense, but I'm not sure whether that's what you want.

I'm not sure if I was being either too basic or too advanced, so please write again if you have additional questions.
Oct 6, 2007 at 7:45 PM
My concepts have been advanced since my first question. I do understand the concept of service locator and dependency injection much clearer now. I have tried very simple code, almost like Hello World example came in QuickStarts of Service Locator 2.0. I did everything same as HelloWorld in my code but unfortunately, after several hours of hit and trial, I figured that it would keep failing in ConvertFrom function of Microsoft.Practices.EnterpriseLibrary.Common.Configuration.AssemblyQualifiedTypeNameConverter class untill I have referenced the service directly into the client application. I just can't understand why its not required in HelloWorld example and why it is necessary in all my trials. I think directly adding the service reference in the client application defeats the purpose of dependency injection and concept of service locator. I wish I could send you the whole code and keep this message readable but seems can't do that so pasting required code. Deeply appreciate your patience on this one.
**************************************************
Services Block from App.config
<add name="IMyService" type="Ploeh.Common.EnterpriseLibrary.ServiceLocator.Configuration.ServiceData, Ploeh.Common.EnterpriseLibrary.ServiceLocator, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null"
serviceType="WindowsApplication1.IMyService, WindowsApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
defaultCreationPolicy="Default">
<creationPolicies>
<add name="Default" type="Ploeh.Common.EnterpriseLibrary.ServiceLocator.Configuration.ConstructorServiceCreationPolicyData, Ploeh.Common.EnterpriseLibrary.ServiceLocator, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null"
constructorType="Services.MyService, Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</creationPolicies>
</add>
*********************************************
class: IMyService.cs , This class is added directly in the client application.
namespace WindowsApplication1
{
public interface IMyService
{
string GetName();
}
}
******************************************
Another class library project called Services having class MyService.cs
namespace Services
{
public class MyService : IMyService
{
public string GetName()
{
return "Message Call of GetName in Service1";
}
}
}
**************************************
Code in Client windows form - button click
IMyService objInterface = ServiceFactory.Create<IMyService>();
MessageBox.Show(objInterface.GetName());
************************************************************

Once again it works only when I add services.dll in my client application. Can you please guess anything, what is that I might be doing wrong? Thanks your very much.

Coordinator
Oct 7, 2007 at 7:23 AM
You don't write what error you are getting, but if you take a look at the code for Microsoft.Practices.EnterpriseLibrary.Common.Configuration.AssemblyQualifiedTypeNameConverter.ConvertFrom, the most obvious thing that can go wrong is the Type.GetType call. That seems to fit pretty well with the rest of the symptoms you describe. To successfully invoke Type.GetType(string), Fusion must be able to locate the type. If the assembly containing the type (MyService, in your case) isn't referenced by your client, it's not automatically included in the output directory, and Fusion isn't going to be able to find it (unless it's in the GAC).

If you look at the project properties for the HelloService in the HelloWorld QuickStart, you will notice that there's a post-build event that copies the service assembly to the output folder of the client, which enables Fusion to locate the assembly when ConvertFrom is invoked. That's one of the drawbacks of late binding - I've described this and a possible solution (not related to Service Locator, though), here: http://blogs.msdn.com/ploeh/archive/2007/05/30/CodeAsDependencyConfiguration.aspx.

You are completely correct that referencing services.dll in your client application (how is that possible, BTW? Doesn't it create a circular reference?) totally defeats the purpose of Service Locator, but if my guess about your symptoms are correct, you should be able to remove that reference and solve your problem by copying service.dll into your client's output folder.

I hope this helps.
Oct 7, 2007 at 4:37 PM
Thanks for your reply. Copying services.dll and pwd in client bin\debug worked using the post build event. I also went thru the link you mentioned of as workaround to dependency files to be copied in the client solution but my initial understanding it may not work for the project I have in needs.

This time I tried something more but stuck again. I am wondering how it is supposd to work in the case where the service component is not local. what I tried to do is to create a web service in a different solution.
The solution where I have the client has a project called 'RemoteProxies' in the same solution so I added reference of my web service in 'RemoteProxies'. Since I am trying to create a scenario more like a real project, I have got mutliple projects (showing the dependencies in the order marked by arrows'->') in the client solution like UserInterface (Windows Application) -> Business Component Layer (Class Library) -> ServiceAdaptor(Another Class Library Which calls web service proxies, refrence to a project called 'remoteProxies') . Now RemoteProxies solution has nothing but reference of the web service 'MyWebServices' created in a different solution. One more project in the same client solution called 'Definition' has couple of business entities which are request and response object for web service calls and an Interface which is same type as web service and is implemented by web service called 'MyWebService' in 'MyWebServices' project. This project 'Definition' is referenced in all the projects in client solution as well as 'MyWebServices' project. I hope this explains what my application looks like. Point of focus is the project 'ServiceAdaptor' which is meant to be the client of web services and supposed to consume them using service factory .
Here is the code for it.
public static ICustomerAccountContactDetail GetCustomerAccountServiceInterface()
{
ICustomerAccountContactDetail iCustomerAccountContactDetail = ServiceFactory.Create<ICustomerAccountContactDetail>();
return iCustomerAccountContactDetail;
}
******************************************
and another important thing you probably need to see is the app.config which is also part of 'ServiceAdaptor' project-:
<add name="ICustomerAccountContactDetail" type="Ploeh.Common.EnterpriseLibrary.ServiceLocator.Configuration.ServiceData, Ploeh.Common.EnterpriseLibrary.ServiceLocator, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null"
serviceType="MOD.WSS.Definition.ICustomerAccountContactDetail, MOD.WSS.Definition, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
defaultCreationPolicy="Default">
<creationPolicies>
<add name="Default" type="Ploeh.Common.EnterpriseLibrary.ServiceLocator.Configuration.ConstructorServiceCreationPolicyData, Ploeh.Common.EnterpriseLibrary.ServiceLocator, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null"
constructorType="MOD.WSS.RemoteComponentProxies.CustomerAccountWSProxy.CustomerAccountWS, MOD.WSS.RemoteComponentProxies, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</creationPolicies>
</add>
*********************************
but I get an error now at
bool created = ServiceFactory.TryCreate(typeToCreate, id, configurationSource, out serviceInstance);
of Ploeh.Common.EnterpriseLibrary.ServiceLocator in public static object Create(Type typeToCreate, string id, IConfigurationSource configurationSource) method call.

error is No object instance was created for the requested service. The type of the service was MOD.WSS.Definition.ICustomerAccountContactDetail. The id of the service was <null>.
************
I have referenced MOD.WSS.Definition.dll and MOD.WSS.RemoteProxies.dll in ServiceAdaptor project? what else am I missing? any thoughts will be greatly appreciated.

Btw, I also replicated the whole scenario by replacing web service proxy reference to an actual solution which has regular class library service. It gave me the same error.

Thanks so much in advance. I wish there were some more intermediate level of code samples of using service factory because Hello World is a bit too simple and StarShip is bit too complex.
Oct 7, 2007 at 4:38 PM


greatvishal wrote:
Thanks for your reply. Copying services.dll and pdb in client bin\debug worked using the post build event. I also went thru the link you mentioned of as workaround to dependency files to be copied in the client solution but my initial understanding it may not work for the project I have in needs.

This time I tried something more but stuck again. I am wondering how it is supposd to work in the case where the service component is not local. what I tried to do is to create a web service in a different solution.
The solution where I have the client has a project called 'RemoteProxies' in the same solution so I added reference of my web service in 'RemoteProxies'. Since I am trying to create a scenario more like a real project, I have got mutliple projects (showing the dependencies in the order marked by arrows'->') in the client solution like UserInterface (Windows Application) -> Business Component Layer (Class Library) -> ServiceAdaptor(Another Class Library Which calls web service proxies, refrence to a project called 'remoteProxies') . Now RemoteProxies solution has nothing but reference of the web service 'MyWebServices' created in a different solution. One more project in the same client solution called 'Definition' has couple of business entities which are request and response object for web service calls and an Interface which is same type as web service and is implemented by web service called 'MyWebService' in 'MyWebServices' project. This project 'Definition' is referenced in all the projects in client solution as well as 'MyWebServices' project. I hope this explains what my application looks like. Point of focus is the project 'ServiceAdaptor' which is meant to be the client of web services and supposed to consume them using service factory .
Here is the code for it.
public static ICustomerAccountContactDetail GetCustomerAccountServiceInterface()
{
ICustomerAccountContactDetail iCustomerAccountContactDetail = ServiceFactory.Create<ICustomerAccountContactDetail>();
return iCustomerAccountContactDetail;
}
******************************************
and another important thing you probably need to see is the app.config which is also part of 'ServiceAdaptor' project-:
<add name="ICustomerAccountContactDetail" type="Ploeh.Common.EnterpriseLibrary.ServiceLocator.Configuration.ServiceData, Ploeh.Common.EnterpriseLibrary.ServiceLocator, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null"
serviceType="MOD.WSS.Definition.ICustomerAccountContactDetail, MOD.WSS.Definition, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
defaultCreationPolicy="Default">
<creationPolicies>
<add name="Default" type="Ploeh.Common.EnterpriseLibrary.ServiceLocator.Configuration.ConstructorServiceCreationPolicyData, Ploeh.Common.EnterpriseLibrary.ServiceLocator, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null"
constructorType="MOD.WSS.RemoteComponentProxies.CustomerAccountWSProxy.CustomerAccountWS, MOD.WSS.RemoteComponentProxies, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</creationPolicies>
</add>
*********************************
but I get an error now at
bool created = ServiceFactory.TryCreate(typeToCreate, id, configurationSource, out serviceInstance);
of Ploeh.Common.EnterpriseLibrary.ServiceLocator in public static object Create(Type typeToCreate, string id, IConfigurationSource configurationSource) method call.

error is No object instance was created for the requested service. The type of the service was MOD.WSS.Definition.ICustomerAccountContactDetail. The id of the service was <null>.
************
I have referenced MOD.WSS.Definition.dll and MOD.WSS.RemoteProxies.dll in ServiceAdaptor project? what else am I missing? any thoughts will be greatly appreciated.

Btw, I also replicated the whole scenario by replacing web service proxy reference to an actual solution which has regular class library service. It gave me the same error.

Thanks so much in advance. I wish there were some more intermediate level of code samples of using service factory because Hello World is a bit too simple and StarShip is bit too complex.


Coordinator
Oct 8, 2007 at 8:45 AM
I can't tell you exactly what your problem is, but the error message you are getting indicates that Service Locator couldn't figure out how to create an instance of the requested type (ICustomerAccountContactDetail in your case).

To create an instance of the requested type, Service Locator goes through the following build stages (defined in ServiceBuilder.cs):
  • Check if the instance was already created by an earlier request and stored in memory as a 'singleton'. If such an instance exists, it's returned; otherwise, the next step is attempted. You have to actively opt in to register these singletons, so it's not the most common scenario.
  • Load the configuration to see whether it contains any information about how to create the instance. This seems to be what you are attempting.
  • If no instance is created, it then examines whether it can infer a constructor to use. Since you are requesting an interface type, this will never work in your scenario, since interfaces don't have constructors.
  • If no instance is created, Service Locator then examines whether a static member exists on the type which can be used to create it. Again, since you are requesting an interface type, this will never work in this scenario.

So, basically, when you are requesting an interface type, configuration is the only option. That's also what you seem to be attempting, but for some reason, it's not working. My guess is that the service you have configured doesn't match the correct type 100%, which means that the ConfigurationCreationStrategy doesn't find the entry for it. From your description, I can't tell you why this is so, but can you try to set a breakpoint in Ploeh.Common.EnterpriseLibrary.ServiceLocator.ConfigurationCreationStrategy.DefinePolicy to see what goes wrong?
Mar 26, 2008 at 9:57 AM


ploeh wrote:
I can't tell you exactly what your problem is, but the error message you are getting indicates that Service Locator couldn't figure out how to create an instance of the requested type (ICustomerAccountContactDetail in your case).

To create an instance of the requested type, Service Locator goes through the following build stages (defined in ServiceBuilder.cs):
  • Check if the instance was already created by an earlier request and stored in memory as a 'singleton'. If such an instance exists, it's returned; otherwise, the next step is attempted. You have to actively opt in to register these singletons, so it's not the most common scenario.
  • Load the configuration to see whether it contains any information about how to create the instance. This seems to be what you are attempting.
  • If no instance is created, it then examines whether it can infer a constructor to use. Since you are requesting an interface type, this will never work in your scenario, since interfaces don't have constructors.
  • If no instance is created, Service Locator then examines whether a static member exists on the type which can be used to create it. Again, since you are requesting an interface type, this will never work in this scenario.

So, basically, when you are requesting an interface type, configuration is the only option. That's also what you seem to be attempting, but for some reason, it's not working. My guess is that the service you have configured doesn't match the correct type 100%, which means that the ConfigurationCreationStrategy doesn't find the entry for it. From your description, I can't tell you why this is so, but can you try to set a breakpoint in Ploeh.Common.EnterpriseLibrary.ServiceLocator.ConfigurationCreationStrategy.DefinePolicy to see what goes wrong?

Coordinator
Mar 26, 2008 at 12:45 PM
You just copied in my last response. Did you also have a question?