Thursday 24 July 2008

WCF In 1 Hour

As part of a product concept study, I needed to prototype a web service to be consumed by a telephony service. This is to simulate a targeted promotion campaign in the telco environment.

The web service allows the consumer to check by keyword for any related promotions. If there are any, the consumer can retrieve the promotion and use the details of the promotion information - e.g. send a SMS or email to the subscriber to notify him/her about the promotion. I had the following in mind about the promotion web service:

  1. to store the promotion data in an xml file
  2. have a boolean service to check whether there are any promotions in the database with matching keyword
  3. have another service method to return the promotion data (an object/composite type) with matching keyword
  4. the promotion information need to be short and concise, making it suitable for sending in a SMS
The XML file I chose to create looked something like this:

 
  New Years Eve Concert
  The biggest event of the year. Artists from all around
  the country.
  Domain
  2008-12-31 20:00:00
  New Year's Eve
 
 
  Batman The Dark Knight
  
   Meet stars in Batman in person - Chritian Bale, Michael Caine.
  
  Star City
  2008-7-30 10:00:00
  Batman, action
 
 
  Jazz Night
  
   Jazz lovers' do not miss this once in a lifetime opportunity.
  
  The Jazz Club
  2008-10-30 21:00:00
  Jazz
 

After a short consideration, I decided to use .NET WCF for the web service and LINQ for the XML querying. The only problem is that I knew nothing about WCF and little about LINQ (I lived in the Java land). But it did not stop me. In fact, it made it more exciting.

The first thing I did was to create a WCF project in VS.2008. VS.2008 made it extremely easy. I then modified the auto-generated service interface and the service implementation files:

The serivce interface is IPromoService

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace svdemo {
    [ServiceContract]
    public interface IPromoService {
        /// 
        /// Tests whether there are any promotions with matching keyword.
        /// 
        /// <param name="keyword">the keyword to be matched. Case in-sensitive, single word only</param>
        /// <returns>whether there are any promotions with matching keyword</returns>
        [OperationContract]
        bool HasPromo(string keyword);

        /// 
        /// Get the first promotion which matches the given keyword.
        /// 
        /// <param name="keyword">case in-sensitive, single word only.</param>
        /// <returns>The first promotion with matching keyword.</returns>
        [OperationContract]
        PromoInfo GetFirstPromo(string keyword);
    }


    // Use a data contract as illustrated in the sample below to add composite types to service operations.
    [DataContract]
    public class PromoInfo {
        string promoName;
        string promoDescription;
        string promoVenue;
        DateTime promoDateTime;

        public PromoInfo(string name, string desc, string venue, DateTime dt) {
            promoName = name;
            promoDescription = desc;
            promoVenue = venue;
            promoDateTime = dt;
        }
        [DataMember]
        public string PromoName {
            get { return promoName; }
            set { promoName = value; }
        }

        [DataMember]
        public string PromoDescription {
            get { return promoDescription; }
            set { promoDescription = value; }
        }

        [DataMember]
        public string PromoVenue {
            get { return promoVenue; }
            set { promoVenue = value; }
        }

        [DataMember]
        public DateTime PromoDateTime {
            get { return promoDateTime; }
            set { promoDateTime = value; }
        }
    }
}

The service implementation PromoService:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Xml.Linq;

namespace svdemo {
    public class PromoService : IPromoService {
        XDocument doc;
        public PromoService() {
            doc = XDocument.Load(@"D:\projects\SigvalueWS\svdemo\svdemo\data.xml");
        }

        public bool HasPromo(string keyword) {
            if (keyword == null)
                return false;

            string kw = keyword.ToUpper();
            var q = from p in doc.Descendants("Promotion")
                    where ((string) p.Element("Tags")).ToUpper().Contains(kw)
                    select p.Element("Name").Value;
            return (q.Count() > 0);
        }

        public PromoInfo GetFirstPromo(string keyword) {
            if (keyword == null)
                return null;
            string kw = keyword.ToUpper();
            var q = from p in doc.Descendants("Promotion")
                    where ((string) p.Element("Tags")).ToUpper().Contains(kw)
                    select new PromoInfo(p.Element("Name").Value,
                        p.Element("Description").Value,
                        p.Element("Venue").Value,
                        DateTime.Parse( p.Element("DateTime").Value));
            PromoInfo promo = q.First();
            return promo;
        }
    }
}

Running this project (Ctrl+F5) will deploy the web service onto a test web server and the wsdl can be obtained from the url http://localhost:2144/PromoService.svc?wsdl

To test the consumption of the web service, I created a console application project in C#. Adding a service reference (by copying the above URL into it) automatically generates the required web service client entry point code, client proxy, and the DTO PromoInfo class. So consuming the service is a no-brainer:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using svdemoClient.ServiceReference;

namespace svdemoClient {
    class Program {
        static void Main(string[] args) {
            PromoServiceClient client = new PromoServiceClient();
            string keyword="Jazz";
            if (client.HasPromo(keyword)){
                PromoInfo promo=client.GetFirstPromo(keyword);
                Console.WriteLine(
                    String.Format("got {0}, {1}", 
                    promo.PromoName, promo.PromoDateTime)
                );
            }else
                Console.WriteLine("nothing");
            System.Threading.Thread.Sleep(3000);
            client.Close();
        }
    }
}

Although this is my very first attempt to use WCF and LINQ to XML, the whole thing took me an hour and a bit and majority of the time was used in googling on how to use LINQ to XML.

Note: it is better to modify the transport setting to use Basic HTTP instead of TCP to improve interoperability. See here for more details.

1 comment:

Karthic Raghupathi said...

Good post. Got me started on WCF. I also liked the article on consuming the service using Java.

Keep up the good work.