April 2007 - Posts

Recently I needed to set up some simple code to demonstrate WCF (as an alternative to some other means of communication in distributed applications). But when I googled around, I could not find a really, really simple WCF example. Sure, there are lots of WCF introductions, but they all explain a lot of stuff I did not really want to know at that time. Also they most often spread the code across many files and even across languages (C# and XML). And that´s what I really, really hate! When I first try out a new API like WCF I want to have everything needed in one (!) place. I want all that´s required to be the minimum and in my favorite programming language. This way I can focus best on what´s really essential without getting distracted by syntax and unnecessary (but cool) "fluff".

Finally I resorted to a book I wrote together with co-author Christian Weyer: ".NET 3.0 kompakt" (German). As you can imagine, I did not write the chapter on WCF, though ;-) In our book I found the most simple/gentle introduction to WCF and finally was able to set up my sample program within some 20 minutes. I only had to experiment a little for getting the client code to run without resorting to XML, since Christian did not show imperative code for it in the book.

Now, to make life easier for you, I present to you the most simple WCF application I can think of in one piece. Copy the pieces into a VS 2005 console project, reference the System.ServiceModel library from .NET 3.0 and off you go. The code should run right out of the box. (Or download it here.)

Although I don´t want to repeat what´s been said in our book or in others like "Programming WCF Services" by Juval Löwy or elsewhere on the web about WCF (e.g. here http://msdn2.microsoft.com/en-us/library/aa480190.aspx or here http://www.devx.com/dotnet/Article/29414 or here http://bloggingabout.net/blogs/dennis/archive/2006/10/18/WCF-Part-0-_3A00_-Introduction.aspx), I think it´s in order to give you a quick tour through my code:

Service definition

    1 using System;

    2 using System.Collections.Generic;

    3 using System.Text;

    4 

    5 using System.ServiceModel;

    6 

    7 

    8 namespace WCFSimple.Contract

    9 {

   10     [ServiceContract]

   11     public interface IService

   12     {

   13         [OperationContract]

   14         string Ping(string name);

   15     }

   16 }

My simple sample service is of course trivial, since I´m not interested in service functionality but getting the WCF infrastructure to run. So my service WCFSimple.Contract.IService just consists of a single method - Ping() - which accepts a name and responds with a greeting. Passing in "Peter" returns "Hello, Peter".

Defining a WCF service means defining its functionality in a so called service contract which is given as an interface here. That this interface is a service contract and which of its methods should be published is signified by the attributes.

Service implementation

   19 namespace WCFSimple.Server

   20 {

   21     [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]

   22     class ServiceImplementation : WCFSimple.Contract.IService

   23     {

   24         #region IService Members

   25 

   26         public string Ping(string name)

   27         {

   28             Console.WriteLine("SERVER - Processing Ping('{0}')", name);

   29             return "Hello, " + name;

   30         }

   31 

   32         #endregion

   33     }

Once you´ve defined a service you can implement it. Just define a class that implements the service´s interface (line 22, 26..30). The ServiceBehaviour attribute in this case tells the WCF infrastructure to create a fresh instance of the service class for each call to the service´s methods. It´s the same as .NET Remoting´s SingleCall wellknown objects. Of course, though, I could have chosen a different instanciation mode, but I wanted to avoid any side effects due to state issues.

Hosting the service, implementing the server

   36     public class Program

   37     {

   38         private static System.Threading.AutoResetEvent stopFlag = new System.Threading.AutoResetEvent(false);

   39 

   40         public static void Main()

   41         {

   42             ServiceHost svh = new ServiceHost(typeof(ServiceImplementation));

   43             svh.AddServiceEndpoint(

   44                 typeof(WCFSimple.Contract.IService),

   45                 new NetTcpBinding(),

   46                 "net.tcp://localhost:8000");

   47             svh.Open();

   48 

   49             Console.WriteLine("SERVER - Running...");

   50             stopFlag.WaitOne();

   51 

   52             Console.WriteLine("SERVER - Shutting down...");

   53             svh.Close();

   54 

   55             Console.WriteLine("SERVER - Shut down!");

   56         }

   57 

   58         public static void Stop()

   59         {

   60             stopFlag.Set();

   61         }

   62     }

   63 }

I chose to set up the server for my service implementation just on a separate thread in my sample app. But to make it easier for you to move it to a process/project of its own, I let it look like the default class of a console application.

In order to host a service, the Main() method creates a ServiceHost to manage any service implementation instances and publish the service on any number of endpoints. I chose to make the service accessible just through one endpoint, though. And this I do explicitly in code - instead of through some App.Config settings. That way I get to know how the WCF parts work together.

The endpoint takes as a first argument the service definition (contract), since the class I passed to the ServiceHost could possibly implement several services. Then I specify the "communication medium", the binding. TCP seems to be suffcient for my purposes. Finally I set up the endpoint at a particular address that surely needs to match the binding. For TCP I specify an IP address (or domain name) and a port.

That´s it. WCF can be as simple as the ABC: Address+Binding+Contract=Endpoint.

Now I just need to start the ServiceHost with Open() and later on finish it with Close(). However, since the service host is doing its work on a separate thread, I cannot just move on after Open(). I have to wait for a signal telling the server to stop. For that purpose I set up a WaitHandle - stopFlag - which can be signalled from the outside through the Stop() method.

If you put the server in a console app of its own, you can remove the WaitHandle stuff and replace the stopFlag.WaitOne() with just a Console.ReadLine().

Warning: The Close() takes at least 10 sec to complete, as it seems. This is to give any still connected clients the chance to disconnect, as far as I understand right now. So if you don´t happen to see the "Shut down!" message when running the sample, don´t worry.

Running the server, implementing the client

   66 namespace WCFSimple

   67 {

   68     class Program

   69     {

   70         static void Main(string[] args)

   71         {

   72             Console.WriteLine("WCF Simple Demo");

   73 

   74             // start server

   75             System.Threading.Thread thServer = new System.Threading.Thread(WCFSimple.Server.Program.Main);

   76             thServer.IsBackground = true;

   77             thServer.Start();

   78             System.Threading.Thread.Sleep(1000);  // wait for server to start up

   79 

   80             // run client

   81             ChannelFactory<WCFSimple.Contract.IService> scf;

   82             scf = new ChannelFactory<WCFSimple.Contract.IService>(

   83                         new NetTcpBinding(),

   84                         "net.tcp://localhost:8000");

   85 

   86             WCFSimple.Contract.IService s;

   87             s = scf.CreateChannel();

   88 

   89             while (true)

   90             {

   91                 Console.Write("CLIENT - Name: ");

   92                 string name = Console.ReadLine();

   93                 if (name == "") break;

   94 

   95                 string response = s.Ping(name);

   96                 Console.WriteLine("CLIENT - Response from service: " + response);

   97             }

   98             (s as ICommunicationObject).Close();

   99             // shutdown server

  100             WCFSimple.Server.Program.Stop();

  101             thServer.Join();

  102         }

  103     }

  104 }

The client in this sample has two tasks: 1) start the server, 2) call the server.

To run client and server in the same process, the server is started in a background thread of its own (lines 75..77). To give it some time to become ready to serve requests, a delay of 1 sec is added afterwards. Also this avoids a mess of messages shown in the console window at start up ;-)

Next the client sets up a channel along which the request can flow between it and the server. To create a channel, a channel factory is needed (line 81f), though. It can create channels for a certain contract, a specific binding, and targeting an address (lines 82..84). Again the ABC of WCF.

Creating a channel with the channel factory then returns a proxy of the remote server which looks like the implementation - but in fact is a channel, i.e. a WCF object (line 86f).

The rest of the code is just a bit of frontend logic: a loop to ask for a name, passing it to the service, and displaying the returned greeting. Just hit enter without a name to end the loop.

Once the loop is terminated, the server is stopped. Before that, though, you should close the proxy! Otherwise the shutdown of the server will take some time, because it first waits for the client.

Summary

That´s it. That´s my very simplest WCF example. It lets you play around with WCF in a single process. Or you can easily distribute the code across several assemblies: one for the server and implementation, one for the client, and one for the contract shared between the two.

Just be sure to download .NET 3.0 and reference System.ServiceModel. The rest is just copying the above code parts in order into a console project and hit F5. (Or download the code here.)

Enjoy!

PS: If you wonder, how I got those pretty listings into my posting, let me point you to CopySourceAsHtml (CSAH): http://www.jtleigh.com/people/colin/software/CopySourceAsHtml/. It´s a great tool for anybody who wants to paste code into a blog at least while using Windows Live Writer. Although I have to switch to HTML view to do that, which is a bit cumbersome, I love both tools.

Jeroen van den Bos bemoaned in a recent post, of how little help automatic software architecture complexity  metrics are to him when assessing effort of a software change. I feel with Jeroen and think, we need to take another look at what complexity means (for software development). Roger Sessions took at stab at this in his article "A Better Path to Enterprise Architecture" - but I disagree with him in some regards.

So, what is complexity about? Why should we care? I like to think of it as a measure for how hard it is to know what impact a change will have on a system. If something is complicated, it´s hard to understand. But if something is complex, it´s hard to manipulate. Since software needs to be understood as well as changed, it should not be more complicated or more complex than necessary. That´s why we should care.

Now, what is it, that can be more or less complex? Complexity is an attribute of systems. And a system is a mesh of elements related to each other:

Elements without any relation to each other, do not constitute a system. They are just a set of elements. But once the elements are connected in some way, a system exists. From a system´s point of view, the elements are even quite neglible. They are black boxes, since the system´s main constituent are the connections and the operations running along those connections. Without connections and operations no system!

Note: To me, software is a system (see my blog series for details (sorry, it´s all German)). It´s a system of so called swolons (short for software holon) on different logical levels of abstraction and manifested in physical artifacts of different "size". Modules (logical units of code), methods/subroutines, types, type instances, assemblies, components, AppDomains, processes: they are all swolons. Swolons are the elements of the system "software" in general.

Elements and operations along connection are not enough to create complexity, though. If they were, then complexity would rise with more elements and more connections. However, more elements and connections alone to me just make a system more complicated. Elements (vertices) and connections (edges) are static. They define the structure/topology of a graph, nothing more.

But complexity has a different quality. Complexity is about dynamics, about change. And change means moving from one state to another. So the notion missing from the picture so far is: state. The state of a system is the sum of the states of its elements.

But it´s not as easy as Roger Sessions depicts it in his article. He claims, a coin and a dice were systems, and a dice was more complex than a coin (see figure below), because a dice has more potential states (6) than a coin (2).


Source: Microsoft

But are coin and dice systems at all? As he depicts them, I´d say no. Where are the elements of the systems? Or if you take a coin as an element, where are the others and where are the connections between the elements?

To Roger, complexity is synonymous with "number of states". That´s why he continous his argument by saying, a "system" of three dice was more complex than just one dice, because it can assume 6*6*6=216 different states.

I, however, don´t think just taking three dice instead of one leads to a more complex system for the simple reason that there is no connection between the three dice. One coin, one dice, three dice... it´s all the same complexity wise. A dice with its 6 states might be more complicated than a coind with its 2 states, and three dice might be more complicated than a single dice. But they all should not be called systems. And I think a single "super dice" with 216 faces would be as complicated as three regular dice.

In order to really start talking about systems, any number of coins or dice need to be connected like in the below figure:


Source: My German Blog

Of course such connections would not make any difference with regard to the number of states. Rather they would make all the difference with regard to changing the state of the system. Because what happens, if I change the state in Roger´s "system" of three non-connected dice? Nothing much. The state of the "system" changes absolutely predictably. If the top face of the dice were 3 and 5 and 1 and thus the "system´s" state 351 and I turn the first dice around, the "system´s" state becomes 451. There is no doubt about it, no hesitation in my thinking, it´s crystal clear to me what will be the state of the "system" after changing state of just one of its elements.

But what if the elements/dice are connected? What if there was a real system like in the above figure? There system E´s state is 222. But what will the system´s state be if I turn the first dice around? Will it really be 522? Maybe, maybe not. It depends on its relation with the second dice. Maybe this relation is a loose one, maybe both dice are connected by just a thin thread. Then turning the first dice probably does not cause the second one to turn too.

But maybe the dice are both connected by a spring. Then turning the first dice surely would affect the state of the second dice. Maybe in a very easy to understand way (e.g. turning the first dice just turns the second, too; 222 would then become 552), but maybe in a not so easy to understand way (e.g. if dice 1 shows an even number before the turn, then the second dice is not turned, otherwise it is; 222 would then become 522, and only with a second turn of dice 1 also dice 2 would turn leading to 252).

A system´s complexity thus is a matter of dependencies between the elements´ states. System complexity is a function of the coupling of the elements along the their relations. To assess the complexity of a system, not only the number of elements and their connections need to be known, but also the "tightness" or "strength" of those connections as well as the direction in which state changes flow along them.

With that in mind back to Jeroen´s trouble with current software architecture complexity metrics. First we need to clarify what we´re talking about: a system of swolons at design time. I think that´s important to note. Swolons as units of code with functionality and state exist in two "realms": design time and runtime. An object clearly is a runtime swolon. It´s class, though, is a design time swolon.

The interesting thing, now, is, state changes flow differently at design time and runtime. At runtime the state of a swolon is made up of its data. State changes in a swolon are induced by messages received by that swolon. Message senders (clients) cause state changes in message receivers (services) - unless a service swolon is stateless. (Returning a result from a service to a client requires both to change their roles; client and service would be connected by a bidirectional relation.) Messages are the operations of runtime systems. Only where messages flow between swolons there is a system at all.

What´s the state of swolons at design time, though? Classes are prototypical design time swolons. But what´s their state at design time? Source code is the design time state of a design time swolon. And the operations along the dependency relations between clients and services are source code modifications.

However, modifications mostly (often?) flow in the opposite direction of a dependency: it is more likely that a client´s source code needs to change if it´s service changes than the other way around - I´d say. But of course, changes in a client might require changes to one of its services.

This all still might seem easy - but what if there are not just 2 or 10 swolons, but 100 or 1000? How many connections are there between them? But most importantly: How do changes to one swolons data (runtime) or one swolons source code (design time) affect other swolons?

I don´t think the real impact of such changes can be calculated yet by any tool for at least three reasons:

  • State changes depend on the initial cause. Changing the same swolon in different ways might cause waves of changes of different size rippling through the mesh of connected swolons.
  • The same cause might lead to different waves of changes depending on the previous state.
  • The tightness of the coupling between swolons is not so much a matter of quantity (e.g. number of connections), but rather a matter of quality. And I don´t know how to assess that automatically. It´s beyond counting anything, it has to do with the meaning of the code, its purpose.

This leads me to the conclusion: Trying to calculate the complexity of a software is a vain effort. From the number of elements and their basic connections only a rough approximation of a system´s complexity can be derived. Complexity is not absolute, but depending on the cause. At best it can only be assessed for a class of causes, I´d say.

So if you want to know the complexity of your software to get a better idea of how much maintenance will cost you... you first need to classify the changes that might need to be applied to the design time swolons constituting the software system. Then you need to "calculate" how far reaching the effects of changes according to those classes would be in the mesh of interconnected swolons.

My bottom line thus is: Do whatever you can to limit the effect of state changes in swolons to keep the complexity of your software system low. Decoupling is one measure in that regard, black box thinking (and code organization) is another measure. Put up "firewalls" between swolons (at design time) so as to hold off changes on one side of it from affecting the other side.

Roger Sessions recommends partioning - and I agree with him. He just does not explain it well. The term "decoupling" does not appear in his article. But what he means is: Partion groups of swolons so that they are as loosely coupled as possible (or not at all); then changes to one such group won´t be able to set another group "on fire". Well, that surely is a very valuable timeless advice.

More Posts