Skip to main content
Email Password   helpLost your password?

Introduction

This article tries to take an in-depth look at how WCF Services can be integrated into an enterprise SOA environment. The SOA is based on a Microsoft UDDI V2 registry that is shipped with Windows Server 2003 and 2008. This article covers the aspects of WCF UDDI integration as well as the creation of dynamic WCF proxies.

Background

Is WCF out of the box a Service Oriented Architecture?

If you think that WCF is an SOA, then let's review part of the definition of "Service Oriented Architecture". OASIS (the Organization for the Advancement of Structured Information Standards) defines SOA as: "A paradigm for organizing and utilizing distributed capabilities that may be under the control of different ownership domains. It provides a uniform means to offer, discover, interact with, and use capabilities to produce the desired effects consistent with measurable preconditions and expectations." Now, let's translate "capability" with "service". So, we get in slightly other words: "An SOA consists of distributed services, some sort of mechanism to offer and find them, a mechanism to interact with the service, and finally, a mechanism to invoke the service." If you try to break down this into web service technology terms, you now translate this into:

So, what part of SOA is covered by WCF?

The service, the interaction, and the service invocation. The runtime discovery is not part of WCF.

Conclusion

WCF offers no proper mechanism to offer and find services at runtime, and hence is a classical 3-tier architecture technology that we basically have has for 15-20 years now. We just called it DCOM or whatever before, and now the same technology gets relabeled with the current buzzword "Service".

Just a service is not an SOA.

WCF follows Web Services

WCF basically combines all the different communication technologies of .NET 2.0 (Remoting, Queues etc.) under one roof and a common programming model. The model itself follows the Web Services model introduced with .NET 2.0. One of the biggest disadvantages of Web Services was that the communication layer for Web Services was pretty much fixed to the WS-I standard (basic HTTP binding in WCF terms). What Microsoft now basically did for WCF was to address this issue and introduce a completely flexible communication layer. So, instead of a pre-assigned WS-I protocol, we can now choose from a wide array of communication protocols that nearly fit every situation. But, from a programmer's point of view, the implementation of WCF services/clients follows pretty much the way we programmed Web Services. On the client side, instead of a "Web Service reference", you now just add a "service reference" to your project, but then, it feels just like before. On the service side, you now have to define a service interface, and you use some other attributes, but then again, it feels like being with Web Services. But, this flexibility has some implications.

Target Enterprise Architecture

Take a look at the following scenario: I have 10 different types of Services running, with 100 clients each. An hour of unscheduled service downtime during day hours can cost me up to a 100 million bucks.

So what are my requirements?

Does standard WCF implementations meet our requirements?

By "standard", I refer to the way the service and the client has been created. Means, setting up the service, then creating a client, adding the service reference to the service, and then calling the service function of interest. What happens if we:

As a result, for every above change, I would need to update 100 hundred client config files, but I have no clue on what workstations the clients are located. As a result, any above change breaks my production process, which is not acceptable in an enterprise environment.

Conclusion

The standard implementation of WCF is not suited for use in enterprise environments.

Introducing a central Service Registry to our Architecture

As a mechanism to find and offer Web Services in an enterprise environment, this rings the bell for UDDI which is included since Windows Server 2003, but which seems to be fairly unnoticed so far. If I say documentation and support is sparse, this is quite an extenuation.

Web Services in a UDDI Environment

Handling web services in a UDDI registry environment is pretty much straight-forward. You simply strip everything but the Types, Message, and Operations sections out of your WSDL, publish it as a model, and then you add the service endpoints to the registry (where there is just one per Service). As a client, you add the Web Service reference to the stripped WSDL, then at runtime, you simply put in the address from the UDDI, and everything is supposed to work just fine as the communication layer is fixed to WS-I and there is just one endpoint.

WCF Services in a UDDI Environment

With WCF, we have a whole bunch of possibilities on how our service can communicate. This includes the option that a service has multiple endpoints offering different communication protocols at the same time. If we have multiple instances of a service, we no longer can assume that they will all use the same communication protocols. But, if we try to handle WCF Services with UDDI just like Web Services, then what happens?

First, we strip everything but the Types, Message, and Operations sections from our WSDL. This is where you stop for the first time... Looking at the WSDL you will find out that the "Types" section is not declared inline, but every namespace just has an import reference. You have three options now.

But, after we make a decision on this first problem now, what happens if you simple exchange the client's ServiceEndPoint address with the URL you get from the UDDI Registry?

What happens if we:

So, if we use any kind of encryption, the concept we used for Web Services fails all but one of our architecture requirements.

Can't WCF Services be integrated into a UDDI environment?

The problem is basically the "ServiceModel" section in our client's config file. It freezes the state of the service to the point where we added the service reference. If you use a binding with encryption, which is by default based on Windows users, you find, for example, a tag called "Identity" which is Windows authentication, usually something like "Hostname\Username", and which is used for the client's encryption to the service. But, once we move the service to another server, this, of course, will fail due to the identity mismatch.

Integration of WCF Services into a UDDI Environment

How to query the Service metadata at runtime?

I remembered I had a little Web Service project where we dynamically queried a service's WSDL, created a DOM object, and then compiled the client at runtime. This project was based on Eshans Golkar's article "Dynamic Discovery and Invocation of Web services". So, my first approach to WCF was similar, and it works. But then, what I basically want to do is more or less the same that the WCFTestClient.exe does. You simply put in a URL to a service, WCFTestClients discovers it, and then creates a client. But, doing a little little reverse engineering using Lutz Roeder's excellent Reflector tool revealed that....

WCFTestClient.exe just invokes svcutil on the command line. :-))).

Who dared to ship an awkward solution like this? Butn when I did the same with "svcutil", I stumbled across two classes in the System.ServiceModel.Description namespace.

MetadataExchangeClient and MetaDataResolver

What both basically do is read the service's WSDL and then return a ServiceEndpointCollection. Now, you just have to choose which endpoint you want to use for communication with your service at runtime. But here, the first questions start. What do I need MetaDataResolver for, that needs a MetadataExchangeClient and returns a ServiceEndpointCollection, when I can use the MetadataExchangeClient's ImportAllEndpoints function? (Update: My implementation is solely based on MetadataexchangeClient. I tried to save some lines of code by using MetaDataResolver, but it never worked out. So, I just can discourage the use of MetaDataResolver.)

If you try get some light into the situation, you will soon find out that in regards to WCF, you now walk off the beaten track. Documentation and references will get very sparse from this point on. But anyway. This is basically exactly what we need to do, if we want to integrate WCF Services into a UDDI environment.

  1. We query the UDDI
  2. We get the main ServiceURL from the UDDI Registry
  3. We consume the service metadata on ServiceURL/mex or ServiceURL?WSDL
  4. We pick an endpoint of choice and configure the binding
  5. We return a client
  6. We use the client to call the desired service function

So, what we basically do is instead of freezing the services state to the point we add the service reference to our client project, we do the "Add Service reference" functionality every first time we want to talk to our service in our code! This basically means we do not need the <ServiceModel> in our config file anymore. This works a while, but then....you will find out...

Why we need the <ServiceModel> Section

The first time a crash happens is when you receive a service response that is larger than 64K. Your client will report that your MaxReceivedMessageSize quota is exceeded. And there, quite frankly, I ask myself the following question: Who the heck decided that in the age of Tera- and Petabytes 64KB would be a good default limit for receiving a service response? My electric toothbrush could handle a response larger than 64KB! The next superfluous default parameter modification that you will encounter is the maxItemsInObjectGraph that has the same arbitrary limit of 64K.

How to modify an unknown binding at runtime?

The core of the problem is that we do not know what bindings the service will offer.

MaxReceivedMessageSize and MaxBufferSize

Let's look at the bindings inheritance. All bindings are derived from the class Binding. But, except for some timeout parameters, there is nothing we can do here. Let's look at the binding element stack. In the stack of Binding Elements, this parameter belongs to the Transport Binding Element, which is part of any binding. But, I tried two days to find a way to get access to the binding elements, but only for CustomBinding will this lead you somewhere. For all other bindings, this is a dead end street. The CreateBindingElements() function will just create a copy of the actual binding elements, and the GetBindingProperty<t>() function which comes along undocumented in official documentation is of no help either. As we have no clue what binding the Service will offer, we now have three options:

  1. The IT-Hall of Shame candidate
  2. Write a function that tries to cast the discovered binding into any known binding and then sets the parameter.

  3. We use Reflection
  4. We use a function that first tries to cast the object into a custom binding. If the cast is successful, it tries for every property of every binding element to set a property with "Name" to "Value", and if the cast fails, just try to find a property with "Name" and then to set it to "Value".

  5. We return a CustomBinding
  6. We use the discovered binding's CreateBindingElement() function, modify the parameter on the TransportBindingElement, and return a CustomBinding instead of the original binding.

I don't like any of these solutions, but finally, we decide to go for the Reflection solution.

How to modify MaxItemsInObjectGraph at runtime?

Interestingly, "MaxItemsInObjectGraph" is a property of the DatacontractSerializer which is also used in all bindings. If you use the config file, you now have to add an Endpoint Behavior of type DataContractSerializer, but if you try to find this in the ServiceModel namespace, you will find out that you can not instantiate this class at runtime. But looking further, there is a DataContractSerializerOperationBehavior that can be instantiated at runtime, but what can only be applied to an operation. So, what I did was to create a class that implements IEndPointBehavior and then simply set this DataContractSerializerOperationBehavior with a useful value to all operations. Now, I could get rid of my <ServiceModel> config section, and all my functions worked just as intended.

So we got it?

If you think this is enough struggling with 64K default parameters (yes, I mean it sarcastically), you are soon to be disappointed. Everything worked now, I could switch my service binding forth and back, and my clients just worked fine until one day..... they stopped working. MetadataExchangeClient reported that "Metadata contains a reference that cannot be resolved". It took me two days to find out what happened. Our data model had gotten bigger and bigger until one day.... Guess it. Yes, the WSDL got larger than 64K. And, this is the point where all reference implementations of MetadataExchangeClient and MetadataResolver will fail. And, guess again. To solve the issue, you have to create a binding on your own, set the MaxReceivedMessageSize parameter, and then use some constructors which are not mentioned in any example.

Finally!

This is so far the last submarine that submerged in front of my target architecture's course. I won't say this was the last issue until the end of the project, but yet, I am confident that we will reach all architecture requirements with WCF without any limitations. So, what happens now if:

All our target architecture goals are met.

Hooray! Awesome.

WCF implemention flaws

Conclusion

Suggestions to Microsoft for next WCF release

Using the code

The code uses the Logging Application Block from Microsoft Enterprise Library 4.1. Either comment out all Logger.Write(...) lines and remove the entries from the config file, or make sure it is installed. I strongly recommend using the Enterprise Library anyway.

The solution comes with four projects.

UDDIServiceFactory

Manages the connection to the UDDI and provides a pattern for client failover. UDDIServiceFactory uses DynamicWCFFactory to create the client. It uses Microsoft.UDDI.dll from the Microsoft UDDI SDK to connect to the UDDI and keep a constant connection within its ManagedURL class.

Configuration parameters:

UDDIServiceFactory looks for the following parameters in app.config:

DynamicWCFFactory

This is used for creating a dynamic WCF client by discovering its WSDL at runtime. It also provides facilities for runtime configuration of a WCF client as well for callback instances in duplex channels. It supports WSDL's larger than 64K.

Configuration parameters:

DynamicWCFFactory is looking for the following parameters in app.config:

WcfService

An out of the box WCF Service. Used for Unit Tests.

DynamicWCFFactoryTests

This is a Unit Test project that contains two tests.

TestDynamicWCFFactory

Creates a WCF client using DynamicWCFFactory and invokes a function of our test service.

[TestMethod]
public void TestDynamicWCFFactory()
{ 
    var dynWCFFactory = 
        new DynamicWCFFactory.DynamicWCFFactory<ServiceReference.Service1Client,
        ServiceReference.IService1>();
    ServiceReference.Service1Client client = dynWCFFactory.CreateProxyFromWSDL(
        "http://localhost:4873/Service1.svc");
    string someString = client.GetData(2);
}

TestUDDIFactory

Creates a WCF client using UDDIServiceFactory and invokes a function of our test service.

Before using the UDDIServiceFactory, you must have set up a UDDI registry and your service must have been registered properly with UDDI.

[TestMethod]
public void TestUDDIFactory()
{
    try
    {
        UDDIServiceFactory<ServiceReference.Service1Client,
            ServiceReference.IService1> uDDIFactory = 
        new UDDIServiceFactory<ServiceReference.Service1Client,
            ServiceReference.IService1>("UDDIURL","serviceKey","bindingKey");
          
        ServiceReference.Service1Client client = uDDIFactory.GetFirstProxy();

        string someString;

        while (true)
        {
            try
            {
                someString = client.GetData(2);
                client.Close();
                break;
            }
            catch (Exception ex)
            {
                // Free Ressources as we are going to create a new proxy
                if (client.State == CommunicationState.Faulted)
                    client.Abort();
                else if (client.State == CommunicationState.Opened)
                    client.Close();
                Logger.Write(ex, new List<string> { "General" }, 1, ERROR_SERVICE_CALL, 
                   System.Diagnostics.TraceEventType.Warning, "Service Call failed.");
                client = uDDIFactory.GetNextProxy();
            }

        }

    }
    catch (Exception ex)
    {
        Logger.Write(ex, new List<string> { "General" }, 1, 
          ERROR_UDDI_FACTORY_CREATION, 
          System.Diagnostics.TraceEventType.Warning, 
          "Creation of UDDIServiceFactory failed.");
        throw;
    }

}

Invoke the Unit Tests over the Visual Studio Test Toolbar. If it is not visible, go to "View -> Toolbars - Test Tools".

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralSecure by Default Pin
Anderson Imes
17:29 11 Aug '09  
GeneralRe: Secure by Default Pin
Tobias Manthey
15:28 12 Aug '09  
GeneralRe: Secure by Default Pin
##
0:20 17 Aug '09  
GeneralNice Article Pin
shanmaor
4:17 10 Jul '09  
QuestionCannot open and execute source code Pin
whithix
3:53 24 Apr '09  
NewsCannot connect to any service. Giving Up error & proxies Pin
Tobias Manthey
11:37 31 Mar '09  
GeneralQuick Question Pin
WhiteKnight
6:41 27 Mar '09  
GeneralRe: Quick Question Pin
Tobias Manthey
8:44 27 Mar '09  
GeneralRe: Quick Question Pin
WhiteKnight
9:30 27 Mar '09  
GeneralRe: Quick Question Pin
Tobias Manthey
11:43 31 Mar '09  
GeneralVery Nice article Pin
andy404
20:40 20 Jan '09  
GeneralReally Nice Article Tobias. Pin
RazanPaul
20:26 13 Jan '09  
GeneralRe: Really Nice Article Tobias. Pin
Tobias Manthey
21:29 13 Jan '09  


Last Updated 27 Jan 2009 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2009