Friday, February 27, 2009

The Visual Studio 2008 Profiler is....

in nice words....

complete crap.

I just upgraded my Visual Studio Professional 2008 Version to a 2800 USD Team Edition. Of course I installed SP1 first. The reason behind was that our WCF Service project is getting to a point where performance is getting relevant, so we needed to get some profiler results on our code.

So if you think you can install Visual Studio Team or Developer Edition and just hit a "Profile Code" button and get some results you are wrong.

For some reason the profiler does not collect any data

"PRF0025: No data was collected"

is all I got the first day. No matter whether I used the "Sampling" or "Instrumentation" method. All I got was straight nothing.

The documentation seems to be close to nothing as well. But then I stumbled into some web page and found out that the whole profiler thing is located in

%PROGRAMFILES%\Microsoft Visual Studio 9.0\Team Tools\Performance Tools

There is a little batch called VSPerfCLREnv.cmd which then can called with various options.
But when you check out this little tool you find out that the whole profiler thing is based on enviroment variables. And guess what you have to do if you set one of the /global parameters...

Yes reboot. Every time.

After that at least could do some profiling with Instrumentation method.

Linq/Lambda Expressions are not handled appropriately

But if the profiler did not collect any data before, now a single service function call collected more than 600MB of data. That's quite a lot if you think that the service function has not more than 100 lines of user code.

It took a while to find out what happened.


foreach (var item in items)
item.StaticInstrument = fullStatics.Find(fs => fs.ID == item.StaticInstrument.ID);

results in 600MB profiler data.

Rewriting the code to use a dictionary instead the List extension method Find(this List)

var itemDic = new Dictionary<string, StaticInstrument>();

fullStatics.ForEach(singleStatic => itemDic[singleStatic.ID] = singleStatic);

items.ForEach(item => item.StaticInstrument = itemDic[item.StaticInstrument.ID]);

results in 2MB of profiler data.

Functions are not displayed appropriatly in the reports.

But then the next disappointment is right behind the door.

In the report I have various multiple summaries like

Function Name:
0x2B000015
0x2B0000oE

This is not really helpful. Especially as the values for function called by the disguised functions are not displayed at all. Everything just gets summed up.

I took me another half day to find out what is the reason behind this.

In the above mentioned directory you find a tool called
VSPerfReport.exe

if you pass call it like

VSPerfReport.exe PATHTO\Report.vsp

I at least got

Warning VSP2701 : Symbol Engine: File '.....dll' could not be found when looking for debug information. Full symbol/type resolution may not be possible

But when you look at the location there is of course the DLL's symbol or pdb file right next to the dll. Why it still fails to load it, I don't know. I did not find any resolution for this problem yet.

So for most of DLL's the report fails to load the symbols. The resulting report is really useless.

I tried to write some mails to the guys from the Microsoft Profiler Team but it took a while to get some answer. See the comment for some suggestions from Marc Popkin-Paine. Unfortunately his suggestions don't work for me and meanwhile the profiler refuses again to collect any data at all.

Here is what happens...

Successfully attached to process: 4828
VSPerformance Automation warning: Warning VSP2348 : 2 buffer(s) lost in association with process 4828.
Exited from process: 4828
Collection file exited: C:\Development\WCFServices\SCDService\SCDServices090305.vsp
Profiler exited
PRF0025: No data was collected.

So if you ever thought about about spending money on Team or Developer Edition to use the profiler.... Save the money. The profiler is complete crap. You even have to be careful with it. I might crash your system with a blue screen of death. The reason behind this is that they obviously access the bios to get the exact number of clock cycles. Because of the different BIOS this is the reason, why it sampling does work for a certain number of systems. But this raises another question. Why the heck can a user process in Windows still read/write the bios and crash the system?

So in short...

With the Microsoft Profiler I wasted a lot of money and time without getting any useful results.

I have evaluated the current ANTS 4.3 profiler. ANTS works right out of the box and here I can see all functions but the disadvantage is a huge overhead. ANTS tries to eliminate the overhead in the calculation but is not totally succesful with this task.

Wednesday, February 18, 2009

Screw Silverlight

The thought behind evaluating Silverlight 2 was to check whether Silverlight is suited to build rich office applications, that can be handled just like common GUI applications but just in IE. Another central question was to verify that I can use my Xaml controls from WPF applications seamlessly in Silverlight 2.
So I am really not interested in fancy design, multimedia and other special effects that seems to be the general target audience for Silverlight 2.

First thing you have to do when you try to evaluate Silverlight 2 with Visual Studio you have to download a whole bunch off applications. Most important it seemed to install the Silverlight Tools for Visual Studio 2008 SP1 .

After downloading some 70 megs of data the the first frustration is right around the corner, if you have to use an authenticating proxy to get Internet access.

Though you have to download some 70 megs the package needs to download more data. And of course there is nowhere a full package. Even more unfortunate it seems that until today Microsoft has never heard anything of a thing called authenticating proxy and consequently the install fails. Sigh.
Call it a act of despair and interest I programmed my own little authenticating proxy bridge that acts as proxy and forwards each request to the real proxy plus adding the required authentication information. So finally I was able to install the tools.

Ok, so I created a new Silverlight project, opened the toolbox, took a button and dropped it onto the designer and...

the designer won't let me do that.

Although we have a fully fledged Xaml designer in WPF, there is absolutely no way to add a control in the designer in Silverlight. You are supposed to drag the items out of the toolbox into the Xaml text editor where it does nothing but adding the following code

<
Button></Button>


Wow, what an awesome and intuitive feature for my 1500 USD Visual Studio.

Ok, lets hand code my button. After setting the button properties width and height you would like some text on your button.

So you code

<Button Width="100" Height="20">Press me</Button>

This is perfect Xaml, even Expression Blend will code it that way, it works fine for WPF, but .....

that is not good enough for Silverlight.

"Button does not support text content" is the error. It turns out that if you want to get it to work with Silverlight you have to code it

<Button Width="100" Height="20" Content="Press me"/>

So let me get this straight.

  • You have to download at least 4 packages to start with silverlight.
  • Even then these packages need to download more data and neither there are full packages available nor does the setup support simple things like authenticating proxies.
    No Internet access means no Silverlight.
  • The nice Xaml designer shipped with WPF had been crippled for Silverlight to be absolutely useless.
  • The Xaml for Silverlight is incompatible with other Xaml.
  • If you do not want to hand code every GUI element and if even if you have the 1500 USD Team Edition of Visual Studio 2008 you need to buy Expression Blend 2 for just another 699 USD.

Ok guys,

this is where my evaluation comes to an abrupt end after just 60 minutes and tell you what....

Screw Silverlight 2.

Wednesday, January 28, 2009

UDDI SOA Howto

This article describes how to set up and configure and Windows Server 2003 UDDI Services.
UDDI Services are IIS hosted web services that require some kind of database.

So before installing UDDI Services you need to have IIS up and running.

Click on the screenshots to get a larger image.

1.) Add UDDI Server Component
Choose
Start - Settings - Control Panel - Add or Remove Programs - Add Remove Windows Compontents

2.) Pick UDDI Services



3.) Choose your Database

Choose the database component of your choice

If you do not have any SQL Server installed choose "Create a new MSDE database instance"
If you have SQL Server 2000 installed choose "Use the following SQL Server 2000 instance"
If you have SQL Server 2005 installed please follow this instruction before choosing "Use the following SQL Server 2000 instance" option



If you get the error message:
"This database instance does not meet the minimum version or Service Pack level requirements and cannot be used for installation. Please upgrade this instance or select another one."


it means you try to install UDDI Services on a server running SQL Server 2005 and you did not follow the instructions.

4.) SSL-encryption
If you have SSL configured for your IIS you can enable this option. If you do not have SSL enabled you can disable it here and enable it later when you SSL enabled your IIS.



5.) Choose setup location

Just choose the path where to install UDDI Services



6.) Choose the user

Choose the user UDDI Services will use



7.) Pick a name for your UDDI




8.) Disable "self register"
This is somehow related to publish the UDDI in Active Directory. I am not really sure what it does so disable it.


Great, we have UDDI Services up and running. Whats next?

Configuration of UDDI virtual directories access

1.) Open Computer Mangement snapin
Choose
Start - Settings - Control Panel - Administrative Tools - Computer Management

2.) Configure IIS "uddi" virtual directory to use Windows Authentication
in the computer management snap in go to
Internet Information Services - Web Sites - Default Web Site

Right click "uddi" and select Properties

Click on Directory Security, disable anonymous access and tick Integrated Windows authentication.

3.) Configure IIS "uddipublic" virtual directory to use UDDI Authentication
in the computer management snap in go to
Internet Information Services - Web Sites - Default Web Site

Right click "uddipublic" and select Properties

Click on Directory Security, enable anonymous access.


Great, we have configured UDDI Service authentication. Whats next?

Create Windows Publishing User account

In standard configuration every user in servers Administrators group has the right to publish Services.
But I really recommend creating a special user account for publishing UDDI service. That has the advantage you get all services published under one user, which gives you a much better overview over your running services.

In this case I created a User "UDDIAdmin" and added him to the "Administrators" group.

Now log in with your "UDDIAdmin" account, open Internet Explorer and type:

Publish a service on UDDI


http://localhost/uddi/

Now Click on Publish on you will see:



First thing we have to do is to create a provider. A "provider" is also referred as a "business" in UDDI terms. Basically the provider or business is just an embracing category for our services.

Create a Provider

Right click on provider and select "Add Provider". Then click on the new provider and edit the name to be "MyProvider".


Create a Service
Now right click on Provider and select "Add Service". Then click on the new service and edit the name to be "MyService".



Create a binding
Now right click on Service and select "Add Binding". Then click on the new binding and edit the URL to be the URL to your Service. "http://myserver/myservice.svc" in this case.


Create a tModel
Now right click on tModels and select "Add tModel". Then click on the new tModel and edit the name to be "MytModel".
What not really required but what makes it more UDDI is the following...
Click on the "Categories" tab and click on "Add Category". Now select "uddi-org:types" "Specification for a web service" "Specification for a web service described in WSDL" and click on "Add Category".
Now click on the "Overview Document" tab. Click on "Edit" and paste a link to the wsdl document of the service. e.g. http://myserver/myservice.svc?WSDL.

You could use these attributes later to pick the right tModel


Add instace Info
Now as a last step right click on your service select "Add instance info". Now type in "My" in the search box click on search and select "MytModel" from the list.

Done. We have published our first service in the UDDI.


Great, but what I do now with my UDDI?

A simple UDDI query in C#

You can install the Windows Server 2003 SDK or simply add a link to Microsoft.UDDI.dll in your C# project. Where to find those is described here.

Now implement the following code:

//URL to your UDDI directory
string uDDIURL = "http://myserver.com/uddi/";

// Take your tModel Key here. Look on the screenshots
string modelKey = "uuid:25f0c31f-1846-4f8c-919b-3164ffae3ed2";

// Take your service Key here. Look on the screenshots
string serviceKey= "49263d91-85e7-4285-b36c-a217d6a8e1b9";


// Create UDDI location object
UddiSiteLocation _siteLocation = new UddiSiteLocation(uDDIURL + "inquire.asmx",
uDDIURL + "publish.asmx",
uDDIURL + "extension.asmx",
string.Empty,
Microsoft.Uddi.AuthenticationMode.WindowsAuthentication);

// Initialize a new instance of the FindBinding class used to locate the service.
// Add the service key to the binding.
FindBinding _findBinding = new FindBinding(modelKey);
_findBinding.ServiceKey = serviceKey;

// Create the managed URL object. It is dynamically updated by the UDDI Registry
UddiConnection _connection = new UddiConnection(_siteLocation);
ManagedURL managedURL = new ManagedUrl(_connection, _findBinding);

if (managedURL.Count > 0)
Console.WriteLine("Hooray we did it");


Please keep in mind the managedURL object now keeps a steady connection with the UDDI and gets information on newly published or removed services. So there is absolutely no need to recreate the manageURL object in order to refresh it.

Further tips regardings UDDI Services can be found here.

For further examples please see this article on codeproject.com which includes some source code.

Tuesday, January 13, 2009

Dealing with Microsoft UDDI services

How do I install and setup Microsoft UDDI Services?

Read the UDDI SOA Howto.

Where to get the UDDI samples?


Microsoft did not include the UDDI samples in the current Windows SDK for Windows Server 2008 and .Net Framework 3.5.

Therefore you need to install the old Windows Server 2003 SDK .

After you installed the Core SDK you find the samples in

%PROGRAMFILES%\Microsoft SDK\samples\UDDI


Where to get Microsoft.UDDI.DLL?

You find it if you install the Windows Server 2003 SDK you find it in

%PROGRAMFILES%\Microsoft SDK\bin

or if you installed .NET 3.0 you find it in

%PROGRAMFILES%\Reference Assemblies\Microsoft\UDDI\v2.1\bin\system32


How to turn on Debugging?


If you want to turn on Debugging use regedit and goto:

[HKLM\SOFTWARE\Microsoft\UDDI\Debug]

set FileLogLevel to the appropriate value. Possible values are:

0 = None
1 = Error,
2 = Warning,
3 = FailAudit,
4 = PassAudit,
5 = Info ,
6 = Verbose

where 6 (Verbose) prints the most information into the file specified by LogFileName

How to configure another virtual directory for UDDI?

Open the IIS Manager, right click on Default Web Site and select New Virtual directory. Select a name for the alias, then select the UDDI/webroot folder (e.g. "c:\inet\uddi\webroot" ). Then select the Read, Run Scripts and Browse permission. After the wizard finishes right click on the virtual directoy and select Properties. Now change the Application Pool to "MSUDDIAppPool". Last thing is to select the ASP.NET tab and change the ASP.NET Version to 1.1.4322.

How to configure Authentication?

Microsoft UDDI offers basically 2 different types of authentication.

Windows Authentication and UDDIAuthentication. The difference is that in Windows Authentication you do not have to specify a Username and Password when you create the UDDIConnection object.

Windows Authentication
In Windows Authentication you do not have to specify a Username and Password. UDDI simply takes the Usercredentials received by the UDDI web service. To configure UDDI for using Windows credentials open the IIS Manager, right click the virtual directory (e.g. uddi or uddipublic), go to the Directory Security tab and click on Authentication and access control. Now make sure "Enable anonymous access" is disabled and Authenticated access is set to Integrated Windows authentication.

Ok, whats UDDIAuthentication?
When you use UDDIAuthentication you specify a Username and Password when you create the UDDIConnection object. However this user has to be a valid windows user account and has to have appropriate permissions. Using UDDIAuthentication the authentication of the account is not enforced by IIS but the UDDI Service will authenticate the user.
To configure UDDI for doing UDDI authentication open the IIS Manager, right click the virtual directory (e.g. uddi or uddipublic), go to the Directory Security tab and click on Authentication and access control. Now make sure "Enable anonymous access" is enabled.

Now use the following pattern:

UddiSiteLocation location = new UddiSiteLocation(
httpServerName + "inquire.asmx",
httpsServerName + "publish.asmx",
httpServerName + "extension.asmx",
"My Site",
AuthenticationMode.UddiAuthentication);

UddiConnection oConnect = new UddiConnection(location, @"Domain\Username", "Password");

oConnect.AutoGetAuthToken = true;

The secret to UDDI Authentication
1.) Try to the current user out the current HTTPContext (Windows Authentication)
2.) Query the Security.Authentication Mode Parameter which is set in the UDDI Database in table UDO_config.
3.) If the Security.Authentication Mode parameter is set to 8 UDDI tries Passport authentication.
4.) Windows Authentication is only used if the current user is not the anonymous user (Anonymous Access is disabled) and you did not specify a username in the connection.
5.) By Default UDDIAuthentication is used.


public AuthToken GetAuthToken(GetAuthToken gat)
{
Debug.Enter();
AuthToken token = new AuthToken();
try
{
IIdentity identity = HttpContext.Current.User.Identity;
int @int = Config.GetInt("Security.AuthenticationMode", 3);
if (8 == @int)
{
if (!(identity is PassportIdentity))
{
throw new UDDIException(ErrorType.E_fatalError, "UDDI_ERROR_PASSPORT_CONFIGURATION_ERROR");
}
Debug.Write(SeverityType.Info, CategoryType.Soap, "Generating credentials for Passport based authentication
dentity is " + gat.UserID);
PassportAuthenticator authenticator = new PassportAuthenticator();
if (!authenticator.GetAuthenticationInfo(gat.UserID, gat.Cred, out token.AuthInfo))
{
throw new UDDIException(ErrorType.E_unknownUser, "USER_FAILED_AUTHENTICATION");
}
if (!authenticator.Authenticate(token.AuthInfo, 0x3840))
{
throw new UDDIException(ErrorType.E_unknownUser, "UDDI_ERROR_USER_FAILED_AUTHENTICATION");
}
if (!Context.User.IsVerified)
{
throw new UDDIException(ErrorType.E_unknownUser, "UDDI_ERROR_NOT_A_VALID_PUBLISHER");
}
}
else if ((!((WindowsIdentity) identity).IsAnonymous && ((@int & 2) != 0)) && Utility.StringEmpty(gat.UserID))
{
Debug.Write(SeverityType.Info, CategoryType.Soap, "Generating credentials for Windows based authentication
Identity is " + identity.Name);
new WindowsAuthenticator().GetAuthenticationInfo(gat.UserID, gat.Cred, out token.AuthInfo);
}
else
{
if ((@int & 1) == 0)
{
throw new UDDIException(ErrorType.E_unsupported, "UDDI_ERROR_AUTHENTICATION_CONFIGURATION_ERROR");
}
Debug.Write(SeverityType.Info, CategoryType.Soap, "Generating credentials for UDDI based authentication");
new UDDIAuthenticator().GetAuthenticationInfo(gat.UserID, gat.Cred, out token.AuthInfo);
}
Debug.Write(SeverityType.Info, CategoryType.Soap, "Windows Identity is " + WindowsIdentity.GetCurrent().Name);
Debug.Write(SeverityType.Info, CategoryType.Soap, "Thread Identity is " + Thread.CurrentPrincipal.Identity.Name);
Debug.Write(SeverityType.Info, CategoryType.Soap, "HttpContext Identity is " + identity.Name);
Debug.Verify(Context.User.IsPublisher, "UDDI_ERROR_NO_PUBLISHER_CREDENTIALS", ErrorType.E_fatalError, new
bject[] { Context.User.ID });
Debug.Write(SeverityType.Info, CategoryType.Authorization, "Authenticated user (userid = " + gat.UserID + " )");
}
catch (Exception exception)
{
DispositionReport.Throw(exception);
}
return token;
}


Errors and Solutions

If authentication fails with "Authetication failed" and in the UDDI log you will see.

FAIL AUTH 2009/01/13 18:09:14 System.ArgumentOutOfRangeException: Length cannot be less than zero.
Parameter name: length
at System.String.Substring(Int32 startIndex, Int32 length)
at UDDI.API.Authentication.UDDIAuthenticator.GetAuthenticationInfo(String userid, String password, String& ticket)

Dont forget to put an @ before the string specifying the username so use:

string szUsername = @"Domain\User";

Monday, January 12, 2009

How to get rid of http://tempuri.org/ in WCF WSDL

"Each XML Web Service needs a unique namespace in order for client applications to distinguish it from other services on the Web. By default, ASP.Net Web Services use http://tempuri.org/ for this purpose. While this suitable for XML Web Services under development, published services should use a unique, permanent namespace." (http://tempuri.org)

By default WCF maps all service operations to the targetnamespace http://tempuri.org/ .
The problem with this approach occurs as soon as you as you need to uniquely indentify a service operation and you have more than just one service. e.g. in Biztalk or Microsoft Managed Service Engine.
Guess you have a 2 services both with the DoSomeThing() function. As both use the http://tempuri.org namespace for their operations, you can't uniquely indentify them.

To get rid of http://tempuri.org/ in your WSDL in WCF do the following:

1.) In your ServiceContract attribute constructor define the Namespace property.

[ServiceContract(Namespace = "http://myservice.com")]

public interface IMyService

2.) For your ServiceClass create the ServiceBehavior attribute with Namespace property

[ServiceBehavior(Namespace = "http://myservice.com")]

class MyService : IMyService

3.) In all your bindings set the bindingNamespace property

<endpoint binding="basicHttpBinding" bindingNamespace="http://myservice.com"....

Friday, December 26, 2008

Thinking WCF towards Enterprise SOA

You want know how to build your own UDDI based SOA?

Read the UDDI SOA Howto.

Update: Get the article including source code at http://www.codeproject.com/KB/WCF/uddiservicefactory.aspx


Is WCF out of the box a Service Oriented Architecture?


If you think that WCF is SOA then lets review part of the definition of "Service Oriented Architecture".

OASIS (the Organization for the Advancement of Structured Information Standards) defines SOA as the following:

"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 desired effects consistent with measurable preconditions and expectations.

Now lets translate "capability" with "service".

So we get in slightly other words: "A 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:

  • Services (this is what WCF is all about)
  • Find and Offer (this could be a UDDI Registry (Enterprise), or WS-Discovery (UPnP like))
  • Interact (this could be WSDL)
  • Invoke (this could be SOAP)

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 for 15-20 years now. We just called it DCOM or Corba or whatever before and now the same technology gets relabeled with the current buzzword "Service". Other examples I love are:
"SAAS" or "Software as a Service" what was called "ASP" or "Application Service Provider" before
"ESB" or "Enterprise Service Bus" what was called "EAI" or "Enterprise Application Integration" before
The thing about buzzwords is that you can throw them around with having no clue what they are really supposed to mean. So the words service in "Service Oriented Architecture" and "Web Service" are commonly misunderstood to be same where in fact they mean two different things.

Just a web service is not a SOA.

WCF follows Web Services

The 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 service model introduced with .Net 2.0. One of the biggest disadvantages of web services was that the communication layer for web services pretty much fixed to WS-I standard (BasicHttpBinding in WCF terms). What Microsoft now basically did for WCF was to address this issue and introduced a completely flexible communication layer. So instead of a preassigned WS-I protocol we can now choose from a widespread of communication protocols that nearly fit every situation.

But from a programmers 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.
  • With every service reference you now add a configuration file with a "ServiceModel" section that keeps all the protocol specific configuration options.
  • The path to the WSDL for web services was pretty much always http://ServiceAddress?WSDL . Now with non http protocols there must be another way to access the Service Metadata. So with WCF the "Mex" or "MetadataExchange" endpoint, that is supposed to be on the "ServiceURL/mex" address, was introduced.

Target Enterprise Architecture

Guess 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?
  • I must be able to relocate my services without any clients failing
  • Services must be redundant and clients must support failover
  • I must be able to dynamically add more services of same type for load balancing and fault safety
  • I want to have a quick overview where my services are running
  • I must be able to change communication parameters (e.g. enable transport encryption on the service, without any of my clients failing

Does standard WCF Implementations meet our requirements?
By "Standard" I refer to the way the the service and the client had 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:
  • Move the service to another server?
    The client fails.
  • A service fails?
    The client fails.
  • Change communication parameters of the service such as binding or encryption?
    The client fails.
  • Want to introduce another service of the same kind for load balancing?
    Installed clients ignore it
  • I want to get a quick overview where my services are running?
    I can't.

As a result for every above change I would need to update 100 hundred clients 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 a 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 a extenuation.

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

WCF Services in a UDDI enviroment
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 handle WCF Services with UDDI just like a Web services then what happens?

At first this means:
First we strip the everything but the Types, Message and Operations sections from our WSDL.
This is where you first time stop. Looking at the WSDL you will find out that the "Types" section is not declared incline, but every namespace just has a import reference.

You have 2 options now.
  • You open all the imports URL's in a browser and then you replace the imports with the results (sucks like hell after the 10th time) or
  • You modify the WSDL behavior of your service with e.g. FlatWSDL. But also this way is not perfect as it sounds as there is a bug in Visual Studio that does not let you choose your array types any more under some circumstances. So if your Service returns an array you could configure it to be a List in your code, but under some circumstances Visual Studio now will not let you change the array type to be anything but array. This is quite nasty as the WSDL is perfectly valid and the type is perfectly recognized as MyType [] but you just will not get is as List anymore, no matter what you select as your ArrayType.

But after we made a decision on this first problem now, what happens if you simple exchange the clients ServiceEndPoint adress with the URL you get from the UDDI Registry.

What happens if we:
  • Move the service to another server?
    The client works if it does not use encryption, otherwise it fails.
  • A service fails?
    The client works if it does not use encryption, otherwise it fails.
  • Change communication parameters of the service such as binding or encryption?
    The client fails.
  • Want to introduce another service of the same kind for load balancing?
    The client works if it does not use encryption, otherwise it fails.
  • I want to get a quick overview where my services are running?
    I get anytime a nice overview from my registry.

So if we use any kind 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 clients 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 usually something like "Hostname\Username" and which is used for the clients 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 services WSDL, created a DOM object and the compiled the client at runtime. So my first approach to WCF was similar, and it works.
But then what I basically want to do is the more or less the same what 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 Roeders excellent Reflector tool revealed that....

WCFTestClient.exe just invokes svcutil on the command line. :-))) . Who dared to ship a awkward solution like this?

But when I did the same with "svcutil" I stumbled across two classes in System.ServiceModel.Description namespace.

MetadataExchangeClient and MetaDataResolver

What both basically do is to read the services 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 MetadataExchangeClients 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. We get the main ServiceURL from the UDDI Registry, then we read the service metadata on ServiceURL/mex or ServiceURL?WSDL, then we pick an endpoint and then we 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 needed the <ServiceModel> section
First thing going to crash 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 fuck decided that in the age of Tera- and Petabytes 64k would be a good default limit for receiving a service response? My electric toothbrush could handle a response larger than 64k. Why not 6k or even better nothing by default??? By the end of the project I am sure it will be more than a thousand times we had to adjust this pain in the ass default parameter. If you are moving on BasicHttpBinding you will need to modify the "MaxBufferSize" parameter as well. Interestingly this parameter has always to be the same as as "MaxReceivedMessageSize". A parameter that has always the same size like another parameter? Jeez...
The next superfluous default parameter modification that you will encounter is the "maxItemsInObjectGraph" that has the same arbitrary limit of 64k.

Ok, I could rant on for while but let's stop crying and look for a solution.

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
Lets look at the bindings inheritance.
All Bindings are derived from class Binding but except from some timeout parameters there is nothing we can do here.
Lets 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 2 days to find a way to get access to the binding elements, but only for CustomBinding this will lead you somewhere. For all other bindings this is a dead end street. The CreateBindingElements() function will just create a copy of the actual bindings elements and the GetBindingProperty() 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 3 options:
  1. The IT-Hall of Shame candidate
    Write a function that tries to cast the discovered binding into any known binding and then sets the parameter
  2. We use Reflection
    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".
  3. We return a CustomBinding
    We use the discovered bindings CreateBindingElement() function, modify the parameter on the TransportBindingElement and return a CustomBinding instead of the original binding.

I don't like any of the solutions but solutions, but finally we decided to 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 in the ServiceModel namespace you will find out that you can not instantiate a this class at runtime. But looking further there is a DataContractSerializerOperationBehavior that can be instantiated at runtime but what can only be applied to a operation.
So what I did, was to create a class that implements IEndPointBehavior and then simply sets 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?

So if you think this is enough struggling with 2 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 "Some items can not be resolved". It took me 2 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 are all reference implementations of MetadataExchangeClient and MetadataResolver will fail. In case of MetadataExchangeClient even with a bolloks error message. And guess again. To solve the issue you have to create a Binding on your own, set this pain in the ass MaxReceivedMessageSize parameter and then use some constructors which are unmentioned in any example.

Finally!

This was so far the last submarine that submerged in front of my target architectures course. I don't say this was the last issue until of 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
  • Move the service to another server?
    I change the address in UDDI and all my clients simply follow and continue working.
  • A service fails?
    The clients does a nice failover and reconnects no matter what.
  • Change communication parameters of the service such as binding or encryption?
    The client adopts to it.
  • Want to introduce another service of the same kind for load balancing?
    Just register it to the UDDI and enjoy
  • I want to get a quick overview where my services are running?
    I get anytime a nice overview from my registry.
All our target architecture goals are met.
Hooray! Awesome.
WCF implemention flaws
  • The 64k default quotas on MaxReceivedMessageSize, MaxBuffersize and MaxItemsInObjectGraph and maybe elsewhere
  • The default WSDL schema that uses "Import" statements in the "Types" section.
  • The bug in Visual Studio that does not let you choose your ArrayType for flat WSDL's (MSConnect ID:387245)
  • The inheritance of bindings that have highly redundant properties in the specific bindings and/or the lack of access to the binding elements for non "Custom Bindings"
  • Inconsistencies between between config file configuration and runtime configuration
  • Lack of documentation
Conclusion:
  • WCF standard implementations are due to their "all is static" approach not Enterprise ready
  • WCF Services can be integrated into a Enterprise SOA architecture but several implementation flaws prevent a seamless integration and increase project risk.
Suggestions to Microsoft for next WCF Release:
  • Default binding quotas
    Quotas should be disabled by default. Any quota you set by default is arbitrary and will never fit all project scopes. If I do not trust the service, its nice that I can set this quota, but please let me choose whats best here. And especially do not use quotas that later blow up your own framework in a timebomb manner.
  • "Imports" in WSDL by default
    This is only causing problems and has absolutely no use. Get rid of it.
  • The inheritance of Binding
    Instead of a 2 level hierarchy it should be much more fine grained. e.g. in class BasicHttpBinding there should be only configuration properties that are absolutely unique to BasicHttpBinding. All others should be inherited. Adding more inheritance could be easily be done without breaking anything. This would smooth the way to a sophisticated runtime configuration of arbitrary bindings. I prefer this much more over a access to the binding elements as in CustomBinding.Elements.
  • Runtime configuration and config configuration should be consistent
If there is some interest I may be able to publish some source code. Just leave a comment.

Wednesday, December 10, 2008

WCF: Binding.GetProperty(T) Method

Did you ever wondered what to put in for the Parameter?
Rather than finding the answer in the Microsoft Documentation you find the answer here.
Basically it seems to be a rather senseless method for the standard bindings.