Building and consuming anything but a trivial web service with Visual Studio can be quite an error prone task. The service itself is difficult to test outside of the web environment, the proxy class generated by Visual Studio will, most of the time, be in a conflicted state in your code repository as multiple developers add and change methods. It’s also quite cumbersome if you want to map those proxy classes back to your own classes. The list goes on and while Windows Communication Foundation does solve some of these issues I’d like to introduce you to the service infrastructure of Spring.Net which can help you to expose your services and is available on .net 2.0.
Let’s first look at the server side. Instead of decorating your service with webservice and webmethod attributes you can write a plain old c# object (poco) which can be tested like any other class and be hosted in multiple environments, exposing the service via remoting is just a configuration change. Nothing even prevents you from making a fat client, just keep the service itself behind an interface.
public class ProductService : IProductService { private List<ProductDto> products; public ProductService() { products = new List<ProductDto>(); products.Add(new ProductDto("Pizza Margherita",9.75)); products.Add(new ProductDto("Pizza Pepperoni Lovers",10.95)); products.Add(new ProductDto("Pizza Cheesam ",10.95)); } #region IProductService Members public RetrieveProductsResponse RetrieveProducts(RetrieveProductsRequest request) { return new RetrieveProductsResponse(products); } public SearchProductsResponse SearchProducts(SearchProductsRequest request) { List<ProductDto> productMatches = new List<ProductDto>(); foreach (ProductDto product in products) { if (product.Name.ToUpper().Contains(request.Name.ToUpper())) { productMatches.Add(product); } } return new SearchProductsResponse(productMatches); } #endregion } |
As you can see, nothing fancy here. The ProductDto, requests, responses and the service interface are all located in an assembly which will be shared between the client and the server.
Whenever you build a service layer around your application, the best way to look upon it is like a GUI. You expose the behaviour of your domain, just not with web or winforms.
Since the service is just a poco, putting it into your spring configuration is just like you would add any other class. You can perform DI, AOP, … upon it like you’d normally do.
<object name="ProductService" type="Service.ProductService, Service"/> |
To make it available as a webservice you use the Webservice exporter which can be found in the Spring.Web assembly as illustrated below.
<object id="ProductWebService" type="Spring.Web.Services.WebServiceExporter, Spring.Web"> <property name="TargetName" value="ProductService"/> <property name="Namespace" value="http://www.bennymichielsen.be/services"/> <property name="Description" value="Defines operations upon products"/> <property name="MemberAttributes"> <dictionary> <entry key="RetrieveProducts"> <object type="System.Web.Services.WebMethodAttribute, System.Web.Services"> <property name="Description" value="Retrieves all products"/> <property name="MessageName" value="RetrieveProducts"/> </object> </entry> <entry key="SearchProducts"> <object type="System.Web.Services.WebMethodAttribute, System.Web.Services"> <property name="Description" value="Searches for products which have a name like the one supplied."/> <property name="MessageName" value="SearchProducts"/> </object> </entry> </dictionary> </property> </object> |
Using the Webservice exporter you can export any object in your configuration as a webservice, just point it to the appropriate object definition using the TargetName property. I’ve populated some more properties just to show how you can use this class. It is however necessary to have an interface(s) on your service, but since this is a best practice anyhow I don’t see this as a shortcoming.
On the client side a similar approach can be used to consume the webservice using the WebserviceProxy factory in the Spring.Services assembly. Using this class your code can depend upon the service interface instead of the proxy class itself. If you want to use the proxy class, this is still supported. In this case Spring will create a proxy which will implement the service interface and map to the Visual Studio proxy. Note that the original service does not have to implement this interface, the methods supplied in the interface are mapped using a kind of duck typing. This is very useful if you don’t control the service but are consuming it and don’t want to tightly couple your code.
If you do control both sides, it is much easier to let the proxy be generated at runtime.
<object name="ProductService" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services"> <property name="ServiceUri" value="http://localhost:1871/ProductWebService.asmx"/> <property name="ServiceInterface" value="ServiceContract.Interface.IProductService, ServiceContract"/> </object> |
This allows you to use the service interface, the concrete implementation is of no concern. What’s also very useful is that you can reuse all the classes which are already in the service contract assembly, thus preventing your solution from being polluted with any additional code.
For more information please take a look at the documentation.