interfaceThe other day I wrote about sharing types between web services to simplify implementation of consumers of these services. You can read about it Multiple web service references sharing typeshere[/intlink]. I mentioned a nicer way to accomplish almost the same thing and I will present it here, by giving an example.

The biggest difference is that you have to define an interface, let all web service producers implement this interface and you as a consumer will be left with a minimal amount of work to consume all these web services as long as their end points are well known.

The scenario

The scenario here is more or less identical to the one in the previous post, but the articles are stored in the consumer instead of in a separate web service. This means that the ArticleOwner is left out. The scenario looks like this:

ws_interface_scenario

As in the previous post, an article will be sent to the right weight calculator based on the type of the article.

The two web services, TypeACalculator and TypeBCalculator, may be referenced using the regular way by adding a web reference in Visual Studio, but the web services will then have its own version of the Article type. A better way to do this is to let the two calculators implement the same interface and as the interface is generated, using the wsdl command line tool, the consumer proxy classes are generated as well.

The web service interface

To generate an interface to be implemented by other services I start off by adding an ASP.NET Web Service project, MyServiceBase, to an empty solution in Visual Studio. By default the project template includes a Service1.asmx with one exposed web method, HelloWorld. I always remove this .asmx file and add a new one. Before adding the new web service, add the same Article class as in the previous post, looking like this:

    public class Article
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public ArticleCharacteristics Characteristics { get; set; }
    }

    public class ArticleCharacteristics
    {
        public double Length { get; set; }
        public double Height { get; set; }
        public double Width { get; set; }
        public double Weight { get; set; }
    }

Now add a new web service to the MyServiceBase project called BaseService.asmx. Remove the HelloWorld method, make the BaseService class abstract and add an abstract method called GetWeight taking an Article as parameter returning a double. The complete code for the abstract web service now looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;

namespace MyServiceBase
{
    ///
    /// Summary description for BaseService
    ///
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX,
    // uncomment the following line.
    // [System.Web.Script.Services.ScriptService]
    public abstract class BaseService : System.Web.Services.WebService
    {
        [WebMethod]
        public abstract double GetWeight(Article article);
    }
}

The next thing to do is to generate the actual interface to be used when implementing the calculator web services, but before that the abstract web service has to be built and started up once, to get an URL to feed the wsdl command line tool with.

The wsdl command line to generate the interface looks like this:

wsdl /serverInterface /out:IBaseService.cs /n:MyServiceBase http://localhost:55617/BaseService.asmx

Note that the port in the URL will most certainly be different in your project.

The generated .cs file is the interface to be implemented by the two calculator web services.

Create two new ASP.NET Web Service application projects, add the generated .cs file containing  the interface. Implement the interface like below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using MyServiceBase;

namespace TypeACalculator
{
    ///
    /// Summary description for TypeA
    ///
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX,
    // uncomment the following line.
    // [System.Web.Script.Services.ScriptService]
    public class TypeA : IBaseServiceSoap
    {
        #region IBaseServiceSoap Members

        public double GetWeight(MyServiceBase.Article article)
        {
            // Let's suppose the density of the article is 1.04/volume unit
            return
                (article.Characteristics.Height *
                article.Characteristics.Width *
                article.Characteristics.Length) * 1.04;
        }

        #endregion
    }
}

TypeBCalculator will implement the same interface in the same way as above. This makes the two calculator web services identical when it comes to their wsdl files. Therefore the same proxy may be used when accessing the services using different endpoint URLs. Have a look below how to generate a nice looking consumer proxy.

The consumer proxies

Since the web services implemented the same interface there is no need to generate proxies for both services. The only thing that is not the same in the two services are the endpoint URLs. The consumer proxy will be generated using the wsdl command line tool, referencing the URL of the BaseService service, BaseService.asmx:

wsdl http://localhost:55617/BaseService.asmx /out:ServiceProxy.cs

The same goes for this URL, it will most certainly be different in your project.

The above command line will generate a .cs file to be included in the consumer project, via “Add existing item…”. The consumer project will be a console application and the project structure looks like below after adding the generated proxy file:

consumer_project

As seen above the TypeACalculator and TypeBCalculator projects are part of the solution but not at all referenced by the Consumer console application.

The code consuming the two web services may look like below, commented below that:

            Article articleForCalcA = new Article()
            {
                Characteristics = new ArticleCharacteristics()
                {
                    Height = 1,
                    Length = 1,
                    Width = 1
                },
                Id = "Art_1",
                Name = "Article for type A calculator"
            };

            Article articleForCalcB = new Article()
            {
                Characteristics = new ArticleCharacteristics()
                {
                    Height = 2,
                    Length = 2,
                    Width = 2
                },
                Id = "Art_2",
                Name = "Article for type B calculator"
            };

            BaseService typeACalculator = new BaseService();
            typeACalculator.Url = "http://localhost:55644/TypeA.asmx";
            Console.WriteLine("Article for type A weighs: " +
                typeACalculator.GetWeight(articleForCalcA).ToString());

            BaseService typeBCalculator = new BaseService();
            typeBCalculator.Url = "http://localhost:61619/TypeB.asmx";
            Console.WriteLine("Article for type B weighs: " +
                typeBCalculator.GetWeight(articleForCalcB).ToString());

Comments on the code:

  • On line 1 and 13 the two articles to be passed as parameters to the services are created and initiated.
  • On line 28 and 33 the web methods are invoked, using the two proxy objects created on line 25 and 30.
  • The only difference between the two services are the URLs on line 26 and 31.

The result

As seen above the resulting code on the consumer side will be as compact as it can be, but of course this is the result of a little bit more work when creating the services to be consumed.

The above steps will be very useful when implementing some kind of subscription service for a huge amount of customers. Let’s consider the following scenario:

  • Some kind of producer creates or gathers and stores data.
  • Subscribers would like to get updates when data is created or modified.
  • The producer defines an interface for the subscribers to implement.
  • The subscribers give the producer the URLs of their services for the producer to push data to.
  • The producer loops through the list of URLs, invoking the same web method using the same parameters for every subscriber.

The above scenario maybe implemented using a Biztalk server or any similar application server, but as seen above the same result will be accomplished in a fairly simple manner. Of course there maybe a need to implement some kind of queue handling if a subscriber is not reached, but this is a different subject and not handled in the scope of this article.

Further more

If there are anything unclear in the article, please don’t hesitate to drop a question! I will give you a notice when I have read the comment and come up with an answer as soon as possible.

Read more about the wsdl command line tool and web service types in the Multiple web service references sharing typesprevious post[/intlink].

  One Response to “Using interfaces when implementing web services”

  1. Hi Fredde..

    I have done the exact same thing as you, but instead of a GetWeight() Method i have a CheckStatus() method that is intended to monitor the health of all our internal Webservices. The thing that differs in this case and that I’m having problems with is the NameSpace.

    You are using the same namespace for all services that you are using the interface on, and in my experience this is a requirement. Or do you have another opinion?

    Tjohej
    Tobbe

 Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

   
© 2011 freddes.se Suffusion theme by Sayontan Sinha