Friday, October 30, 2009

WCF Service in IronPython

Edit 17. 11. 2009: See the article about WCF service in pure IronPython.

Until IronPython 2.6, it was not possible to create WCF service host in pure IronPython. The closest way was to create stub in C# and subclass it in IronPython or create the whole service in C# and run it from IronPython. It is now much simpler with IronPython 2.6 although you still have to write a little C# code.

Simple WCF service implemented in C# looks like this:

TestServiceInterface.cs

using System;
using System.ServiceModel;

namespace TestServiceInterface
{
    [ServiceContract]
    public interface ImyService
    {
        [OperationContract]
        string GetData(int value);
    }
}

TestService.cs

using System;
using System.ServiceModel;
using TestServiceInterface;

namespace myWcfService
{
    public class myService : ImyService
    {
        public string GetData(int value)
        {
            return string.Format("You entered: {0}", value);
        }
    }

    public class mySvc
    {
        public static void Main()
        {
            ServiceHost sh = new ServiceHost(
                typeof(myService),
                new Uri("http://localhost:9000/myWcfService"));
            sh.AddServiceEndpoint(
                typeof(ImyService),
                new BasicHttpBinding(),
                "");
            sh.Open();
            Console.WriteLine("Press  to terminate\n");
            Console.ReadLine();
            sh.Close();
        }
    }
}

You build it:

csc /target:library TestServiceInterface.cs
csc /r:TestServiceInterface.dll TestService.cs

The reason I put TestServiceInterface into separate file is that you cannot create interfaces in IronPython. So this is the only part written in C# when implementig WCF service in IronPython.

The implementation then looks like this:

TestService.py

import clr
import clrtype
clr.AddReference('System.ServiceModel')
clr.AddReference('TestServiceInterface')
from TestServiceInterface import ImyService
from System import Console, Uri
from System.ServiceModel import (ServiceHost, BasicHttpBinding,
        ServiceBehaviorAttribute, InstanceContextMode)
ServiceBehavior = clrtype.attribute(ServiceBehaviorAttribute)

class myService(ImyService):
    __metaclass__ = clrtype.ClrMetaclass
    _clrnamespace = "myWcfService"
    _clrclassattribs = [
            ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]

    def GetData(self, value):
        return "IronPython: You entered: %s" % value

sh = ServiceHost(
        myService(),
        Uri("http://localhost:9000/myWcfService")
    )
sh.AddServiceEndpoint(
        clr.GetClrType(ImyService),
        BasicHttpBinding(),
        "")
sh.Open()
Console.WriteLine("Press  to terminate\n")
Console.ReadLine()
sh.Close()

The myService class must have InstanceContextMode.Single ServiceBehavior attribute because we are passing service instance to the ServiceHost constructor. This is done via new __clrtype__ metaclass. See the error if we don't use the attribute. I was not able to pass type into the ServiceHost constructor.

To test the service, you can use C# or IronPython client implementation:

TestClient.cs

using System;
using System.ServiceModel;
using TestServiceInterface;

namespace myWcfClient
{
    public class cli
    {
        public static void Main()
        {
   ChannelFactory mycf = new ChannelFactory(
     new BasicHttpBinding(),
        new EndpointAddress("http://localhost:9000/myWcfService"));
   ImyService wcfcli = mycf.CreateChannel();
   Console.WriteLine("Calling GetData(33) returns:\n{0}", wcfcli.GetData(33));
        }
    }
}

TestClient.py

import clr
clr.AddReference('System.ServiceModel')
import System.ServiceModel
clr.AddReference('TestServiceInterface')
from TestServiceInterface import ImyService

mycf = System.ServiceModel.ChannelFactory[ImyService](
        System.ServiceModel.BasicHttpBinding(),
        System.ServiceModel.EndpointAddress("http://localhost:9000/myWcfService"))
wcfcli = mycf.CreateChannel()
print "WCF service returned:\n%s" % wcfcli.GetData(11)

Disadvantages:

  • You can have only single instance of the service because you are passing the service instance instead of service type.
  • You cannot easily use .config file to configure your service.