Quantcast
Viewing latest article 8
Browse Latest Browse All 10

TaskCompletionSource and WCF Discovery

For the G.NET in Boston last week I needed to setup a demo for the WCF 4.0 Announcement and Discovery pieces. After sitting through the Parallel Task library talks I was inspired to try to come up with a way for the client to start trying to discover the service while at the same time the service is trying to announce itself. Whichever one finishes first is the one the client will use. I couldn’t actually get it done before the demo because I ran out of time, but I did manage to get it finished later. So here are the results.

First setup the service with an announcement behavior:

<system.serviceModel>
	<services>
		<service name="GenericService.GenericService"
			behaviorConfiguration="mex">
			<endpoint
				address=""
				binding="basicHttpBinding"
				contract= "Interface.IUniversalContract"/>
			<endpoint name="discoEp"
				kind="udpDiscoveryEndpoint" />
		</service>
	</services>
	<behaviors>
		<serviceBehaviors>
			<behavior name="mex">
				<serviceDiscovery>
					<announcementEndpoints>
						<endpoint kind="udpAnnouncementEndpoint"/>
					</announcementEndpoints>
				</serviceDiscovery>
				<serviceMetadata httpGetEnabled="true"/>
				<serviceDebug includeExceptionDetailInFaults="true"/>
			</behavior>
		</serviceBehaviors>
	</behaviors>
</system.serviceModel>

The tricky part is in the client:

First setup an announcement service so that we can hear when incoming announcement messages arrive.

Here is the field:

static TaskCompletionSource<EndpointAddress> tcsFound;

Here is the code to set it up:

tcsFound = new TaskCompletionSource<EndpointAddress>();

var announceSvc = new AnnouncementService();
announceSvc.OnlineAnnouncementReceived +=
	OnOnlineAnnouncementReceived;
announceSvc.OfflineAnnouncementReceived +=
	OnOfflineAnnouncementReceived;
var announceHost = new ServiceHost(announceSvc);
announceHost.AddServiceEndpoint(new UdpAnnouncementEndpoint());
announceHost.Open();

And the delegate which completes the task:

static void OnOfflineAnnouncementReceived(object sender, AnnouncementEventArgs e)
{
	Console.WriteLine("Service is now offline");
}
static void OnOnlineAnnouncementReceived(object sender, AnnouncementEventArgs e)
{
	Console.WriteLine("Service is now online");
	tcsFound.TrySetResult(e.EndpointDiscoveryMetadata.Address);
}

So that it one part. Now the other part is actively going out to find the service.

Here is the field:

static Task<EndpointAddress> findTask;

Here is the code to set it up:

findTask = new Task<EndpointAddress>(() => {
	EndpointAddress localAddress = null;
	do
	{
		localAddress = FindService();
	} while (localAddress == null);
	return localAddress;
});

And lastly the method to actually find the service…

private static EndpointAddress FindService()
{
	var discoProxy = new DiscoveryClient(new UdpDiscoveryEndpoint());
  	var fc = new FindCriteria(typeof(IUniversalContract));
  	FindResponse fr = discoProxy.Find(fc);
	discoProxy.Close();
  	int count = fr.Endpoints.Count;
	foreach (EndpointDiscoveryMetadata item in fr.Endpoints)
	{
		Console.WriteLine(item.Address);
	}
  	if (count > 0)
		return fr.Endpoints[random.Next() % count].Address;
	return null;
}

With those two pieces in play the only thing left is to call the service when either of the tasks completes first.

var tasks = new Task<EndpointAddress>[] { findTask, tcsFound.Task };
int index = Task.WaitAny(tasks);
EndpointAddress address = tasks[index].Result;
InvokeService(address);

I will leave the implementation of InvokeService up to the reader, but the rest of it is done – pretty sweet!


Viewing latest article 8
Browse Latest Browse All 10

Trending Articles