1

I currently am running some WCF REST services in a Windows Service (not IIS), using the WebServiceHost. I have a separate interface and class defined for each service, but I'm having some issues understanding how WebServiceHost, ServiceEndpoint and ServiceContracts can be used together to create a selfhosted solution.

The way that I currently set things up is that I create a new WebServiceHost for each class which implements a service and use the name of the class as part of the URI but then define the rest of the URI in the interface.

[ServiceContract]
public interface IEventsService
{
    [System.ServiceModel.OperationContract]
    [System.ServiceModel.Web.WebGet(UriTemplate = "EventType", ResponseFormat=WebMessageFormat.Json)]
    List<EventType> GetEventTypes();

    [System.ServiceModel.OperationContract]
    [System.ServiceModel.Web.WebGet(UriTemplate = "Event")]
    System.IO.Stream GetEventsAsStream();
 }

public class EventsService: IEventsService
{
     public List<EventType> GetEventTypes() { //code in here }
     public System.IO.Stream GetEventsAsStream() { // code in here }
}

The code to create the services looks like this:

Type t = typeof(EventService);
Type interface = typeof(IEventService);

Uri newUri = new Uri(baseUri, "Events");
WebServicesHost host = new WebServiceHost(t, newUri);
Binding binding = New WebHttpBinding();
ServiceEndpoint ep = host.AddServiceEndpoint(interface, binding, newUri);

This works well and the service endpoint for each service is created at an appropriate url.

http://XXX.YYY.ZZZ:portnum/Events/EventType http://XXX.YYY.ZZZ:portnum/Events/Event

I then repeat for another service interface and service class. I would like to remove the Events in the Url though but if I do that and create multiple WebServiceHosts with the same base URL I get the error:

The ChannelDispatcher at 'http://localhost:8085/' with contract(s) '"IOtherService"' is unable to open its IChannelListener

with the internal Exception of:

"A registration already exists for URI 'http://localhost:8085/'."

I'm trying to understand how the WebServiceHost, ServiceEndpoint and ServiceContract work together to create the ChannelListener.

Do I need a separate WebServiceHost for each class which implements a service? I don't see a way to register multiple types with a single WebServiceHost

Secondly, I'm passing in the interface to the AddServceEndpoint method and I assume that method checks the object for all of the OperationContract members and adds them, the problem is how does the WebServiceHost know which class should map to which interface.

What I would love would be an example of creating a WCF self hosted service which runs multiple services while keeping the interface and the implementation classes separate.

bpeikes
  • 3,495
  • 9
  • 42
  • 80

4 Answers4

3

Sounds to me like the problem that you are having is you are trying to register more than one service on the same service URI. This will not work, as you have noticed, each service must have a unique endpoint.

Unique By

  • IP
  • Domain
  • Port Number
  • Full URL

Examples

http://someserver/foo  -> IFoo Service   
http://someserver/bar  -> IBar Service

http://somedomain  -> IFoo Service   
http://someotherdomain  -> IBar Service 

http://somedomain:1  -> IFoo Service  
http://somedomain:2 -> IBar Service  

You get the idea.

So to directly address your question, if you want more than once service to be at the root url for you site, you will have to put them on different ports. So you could modify your code to be something like

public class PortNumberAttribute : Attribute
{
    public int PortNumber { get; set; }
    public PortNumberAttribute(int port)
    {
        PortNumber = port;
    }
}

[PortNumber(8085)]
public interface IEventsService
{
    //service methods etc
}


string baseUri = "http://foo.com:{0}";
Type iface = typeof(IEventsService);
PortNumberAttribute pNumber = (PortNumberAttribute)iface.GetCustomAttribute(typeof(PortNumberAttribute));
Uri newUri = new Uri(string.Format(baseUri, pNumber.PortNumber));

//create host and all that
iamkrillin
  • 6,798
  • 1
  • 24
  • 51
  • Thanks for the explanation. Not thrilled with the answer, but at least it's clear now. The issue is that I have a separate class for handling each type of resource, i.e. IVenueService defines a function GetVenues with the UriTemplate = "Venues", and IUserService defines a function GetUsers with the UriTemplate = "Users". The problem I have is that I don't want to have to access them via 'http://foo.com:123/Something/Venues' and 'http://foo.com:123/SomethingElse/Users'. I suppose I could just remove the UriTemplates and depend on code that starts up all of the WebServiceHosts to set baseURL. – bpeikes Apr 11 '14 at 19:03
2

I think it might be useful for you to re-think about your URI approach. Uri is a unique resource identifier. Each your endpoint says that you try to expose outside a different kind of resource it's "Events" and "OtherResource". Thus you need to change your UriTemplates a bit.

I would make it so:

[ServiceContract]
public interface IEventTypesService
{
    [OperationContract]
    [WebGet(UriTemplate = "", ResponseFormat=WebMessageFormat.Json)]
    IList<EventType> GetEventTypes();

    [OperationContract]
    [WebGet(UriTemplate = "{id}")]
    EventType GetEventType(string id);
}

[ServiceContract]
public interface IEventsService
{
    [OperationContract]
    [WebGet(UriTemplate = "")]
    Stream GetEventsAsStream();

    [OperationContract]
    [WebGet(UriTemplate = "{id}")]
    Event GetEvent(string id);
}

public class EventsService: IEventsService, IEventTypesService
{
     public IList<EventType> GetEventTypes() { //code in here }
     public EventType GetEventType(string id) { //code in here }
     public Stream GetEventsAsStream() { // code in here }
     public EventType GetEventType(string id) { // code in here }
}

Type t = typeof(EventService);
Type interface1 = typeof(IEventsService);
Type interface2 = typeof(IEventTypesService);

var baseUri = new Uri("http://localhost");
Uri eventsUri= new Uri(baseUri, "Events");
Uri eventTypesUri= new Uri(baseUri, "EventTypes");
WebServicesHost host = new WebServiceHost(t, baseUri);
Binding binding = New WebHttpBinding();
host.AddServiceEndpoint(interface1, binding, eventsUri);
host.AddServiceEndpoint(interface2, binding, eventTypesUri);

And yes, you are right - you have to have different addresses, but it's really different resources. To understand it better you can refer: RESTful API Design, best-practices-for-a-pragmatic-restful-api

To finish, there is a way to use the same address, but the approach a bit weird: Using the same address

Igor Tkachenko
  • 1,120
  • 7
  • 17
  • That's what I was afraid of. I was hoping to keep more of the address defined in the interface, but it does appear that I need to have multiple WebServicesHost and define the base for each resource in the code to setup the services instead of in the interface. – bpeikes Apr 12 '14 at 03:15
  • You can try Web API, it can fits you needs better. A lot of people avoid using WCF for REST and use Web API, which was designed specifically for this purpose. Here is a link: http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api – Igor Tkachenko Apr 12 '14 at 07:22
  • Thanks for the link. I've looked at Web API, but when I've tried starting a self host Web API project there is so much unneeded code added. It seems overkill, which is an odd thing to say considering I'm comparing it with WCF, which seems a bit heavy on it's own. – bpeikes Apr 14 '14 at 12:55
0

The following solution:

  • allows a single object to handle a specific endpoint
  • no part of the path is in the URI template
  • uses the same port for all of the services

It does requires more than one WebServiceHost - one per object that handles requests. Another difficulty is that adding deeper endpoints (like /events/2014) means they either need to have unique parameters or the URI template must include part of the path, if you go convention over configuration that shouldn't be a problem.

A WebServiceHost can only host one thing (class) but that object can have multiple interfaces to handle multiple different types of requests on different URLs. How can different WebServiceHosts bind to the same domain:port? They can't so I guess WebServiceHost wraps an underlying static object that routes requests to the right object. This doesn't technically answer your question but I think this implementation allows you to do what you want right?

A console app that hosts the web services.

public class Program
{
    static void Main (string[] args)
    {
        var venueHost = new WebServiceHost (typeof (Venues));
        venueHost.AddServiceEndpoint (typeof (IVenues), new WebHttpBinding (), "http://localhost:12345/venues");
        venueHost.Open ();

        var eventHost = new WebServiceHost (typeof (Events));
        eventHost.AddServiceEndpoint (typeof (IEvents), new WebHttpBinding (), "http://localhost:12345/events");
        eventHost.Open ();

        while (true)
        {
            var k = Console.ReadKey ();
            if (k.KeyChar == 'q' || k.KeyChar == 'Q')
                break;
        }
    }
}

The Venues class implements IVenues and handles any requests to http://localhost:12345/venues/

    [ServiceContract]
    public interface IVenues
    {
        [WebInvoke (Method = "GET", UriTemplate = "?id={id}")]
        string GetVenues (string id);
    }

    public class Venues : IVenues
    {
        public string GetVenues (string id)
        {
            return "This would contain venue data.";
        }
    }

The Events class implements IEvents and handles any requests to http://localhost:12345/events/

    [ServiceContract]
    public interface IEvents
    {
        [WebInvoke (Method = "GET", UriTemplate = "?venue={venue}")]
        string GetEvents (string venue);
    }

    public class Events : IEvents
    {
        public string GetEvents (string venue)
        {
            return "This would contain event data.";
        }
    }
-1

WCF self hosting can be done in many ways like Console application hosting, Windows service hosting, etc.

I had tried to host two services using a single console application. The structure of the services was similar to what you mentioned, that is, separate classes and interfaces for both the services.

You might want to have a look at this link: Hosting two WCf services using one console app

Community
  • 1
  • 1
AnkitMittal
  • 166
  • 2
  • 18
  • I have a Windows Service which is hosting multiple WCF service classes. The issue I'm having is with how `WebServiceHost` and `ServiceEndpoint` use the information in my `ServiceContract` interfaces to create the ChannelDispatchers. I'm not simply asking how to set up WCF self hosting. – bpeikes Apr 11 '14 at 15:16