Sunday, October 21, 2012

Replacing tempuri.org in a WCF Web Service


I keep forgetting how to do get rid of tempuri.org from a WCF web service.  And I also forget how to remove "Field" from the end of the properties on my class. This post deals with both.

Firstly, removal of tempuri.org I got form this blog.
The steps again are

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

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

class MyService : IMyService

3.) In all your bindings set the bindingNamespace property

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

4.) To remove the word "Field" from the end of the properties

In your ServiceContract attribute constructor define the XmlSerializerFormatAttribute  property.

[Systme.ServiceModel.XmlSerializerFormatAttribute()]

public interface IMyService

5.) If you have an array of objects defined in your class and you return that array in your ServiceContract the previous step will give you an "Array" prefix.  To remove this in your ServiceClass comment out the line

//[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = http://myservice.com)]
 
 
 

Monday, October 1, 2012

WCF Web Services called from BizTalk

I have a BizTalk orchestration which is calling a WCF Web Service.  Now I'm processing lots of messages at once (about 8,000 at a time) but the web service was a bottle neck.
 
During testing we noticed that only 10 connections were opened to the web service at any one time.  As a result we were getting timeout issues and orchestrations were getting dehydrated.  We tinkered around in IIS but try as we might we could not figure out a way of increasing the number of connections. 

Then my colleaguse found this article which reports much better performance if you use net.tcp rather than http and the former has binding properties that can control the number of connections. You need to be cautious because there are quite a lot of factors to consider before changing protocols.  The advice is always to perform tests to determine which is the right protocol to use. 

I've not used net.tcp before so there are a couple of things you need to do first.
1. This will only work on Windows Server 2008 and R2

2. You need to install the feature of .Net 3.5 Framework WCF Activation to support non-HTTP protocols.  By default this is not selected, so you will need to go and install it from Server Manager.

3. One you have done that, you can add the net.tcp biding to the web site where your WCF web service is installed.  Note: keep both protocols: net.tcp and http for the web site.  Do this by editing the bindings and adding net.tcp with Binding Information like 80:*. But also go to Advanced Settings and change Enabled Protocols to htp,net.tcp.

4. Next you need to add the binding to the web.config of the WCF web service.  I did this using NotePad.  Keep the original basicHTTP binding in place because that way you can still check the the web service will load in the browser and you can see the WSDL. I've provided an extract from my web.config below.

<bindings> <netTcpBinding> <binding closeTimeout="00:01:00" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="100" maxReceivedMessageSize="65536" name="NetTcpBinding_IADService" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" transferMode="Buffered"> <readerQuotas maxArrayLength="16384" maxBytesPerRead="4096" maxDepth="32" maxNameTableCharCount="16384" maxStringContentLength="8192"/> <security mode="None"> <transport clientcredentialtype="None" protectionLevel="None"/> <message clientCredentialType="UserName"/> </security> </binding> </netTcpBinding> </bindings>

<service name="WCFADService.ADService" > <host> <baseAddresses> <add baseAddress="net.tcp://localhost/WCFADService/ADService.svc"/> <add baseAddress="http://localhost/WCFADService/ADService.svc/mex"/> <add baseAddress="http://localhost/WCFADService/ADService.svc"/> </baseAddresses> </host> <endpoint address="" binding="netTcpBinding" contract="WCFADService.IADService" bindingConfiguration="NetTcpBinding_IADService" /> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service>

As far as the endpoint url for a net.tcp web service is concerned you can just replace the http:// with net.tcp://

You can test this in the usual way with a console application by using the net.tcp endpoint in the service reference. 

As far as consuming the net.tcp web service in BizTalk is concerned you can use the same consume web service wizard.  That will give you a Custom Binding xml file which you can import into BizTalk.  The magic is then when you click on Configure of the Send Port you get the ability to change the number of connections.
There are two properties here maxConnections and listenBacklog and we set them both to 50.  Sure enough when we tested this out I got 50 simultaneous connections. 

One last point is that running the web service like this hammered the CPU.  You would be well advised to put your web service on another server separate from the BizTalk Server. 



Saturday, March 17, 2012

Logging SOAP messages for a WCF Web Service

I had a need to log inbound SOAP messages to a WCF Web Service. It turns out this is really easy to do by adding some entries in the web.config.  Please note this only works with WCF services, not the old style ASMX web services. 

As always take a backup of the web.config before making changes.

Find the </system.servicemodel>tag and then just above it add

<diagnostics>
<messagelogging logentiremessage="true" logmalformedmessages="false" logmessagesatservicelevel="true" logmessagesattransportlevel="false" maxmessagestolog="3000" maxsizeofmessagetolog="3000" />
</diagnostics>

Then at the bottom of the web.config just before </configuration>add this

<system.diagnostics>
<trace autoflush ="true" />
<sources>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add initializedata="c:\PDFOutput\messages.svclog" name="messages" type="System.Diagnostics.XmlWriterTraceListener"/>
</listeners>
</source>
</sources>
</system.diagnostics>


Be sure to use the svclog extension.  Recycle your WCF service and send some messages through. 
Now you are going to find the svclog hard to read unless you have the SVCTraceViewer.EXE 
Its part of the Windows SDK and well worth downloading.

Monday, March 5, 2012

Deploying Web Sites with an MSI

I have blogged before about using MSIs to deploy but this post deals specifially with deploying a web site and its files to a target server.  This post assumes you are deploying to a Windows Server 2008 (R2) server with IIS 7.5 already installed.  The beauty about using this MSI is it will create a functioning web site for you by the time you click Finish.  Uninstall will remove the web site.  It uses the APPCMD utility which is located in  system32\inetsrv\ on the target server. 
I started with a generic Setup project that I could re-use.  I call this the WebSiteInstaller. In the class that inherits from System.Configuration.Install.Installer, I added an OnAfterInstall override procedure. What it does is uses 2 parameters that are prompted for during installation and passes them to a CreateWebsSite batch file. These 2 paramaters are the Physical path for the web site directory (e.g. c:\inetpub\wwwroot) and the port number. I also added an Uninstall procedure that will call DeleteWebsSite.BAT.

protected override void OnAfterInstall(IDictionary savedState)
{
base.OnAfterInstall(savedState);

// Retrive the target path of the Installation directory
string targetPath = this.Context.Parameters["targetdir"];

// Retrive the Physical path of the web Site
string phyPath = this.Context.Parameters["phyPath"];

// Save the Physical Path in the save state.
savedState.Add("phyPath", phyPath);

// Retrieve the Port of the WebSite.
string websitePort = this.Context.Parameters["webSitePort"];
string arg = @"""" + phyPath + @"""" + " " + websitePort + " " + @"""" + targetPath;

// Execute the CreateWebsSite batch process to create all the components related to WebSIte
ExecuteProcess(targetPath + "CreateWebsSite.BAT", arg);
}

public override void Uninstall(IDictionary savedState)
{
base.Uninstall(savedState);

// Retrive the target path of the Installation directory
string targetPath = this.Context.Parameters["targetdir"];

// Retrive the Physical path of the web Site
string phyPath = Convert.ToString(savedState["phyPath"]);
string arg = @"""" + phyPath;

// Execute the DeleteWebSite batch process to delete all the components related to WebSIte
ExecuteProcess(targetPath + "DeleteWebsSite.BAT", arg);
}
For the source to the ExecuteProcess function, see my earlier post .Thats it for the generic WebSiteInstaller project.
So now I can go to my Web Site Solution and add the WebSiteInstaller project and another Setup project. In my Setup project I add the Primary Output of my web site project and the WebSiteInstaller. By the way, when creating your web site don;t use File -> New -> Web but instead use File -> New -> Project -> Web -> ASP.NET Web Application. The latter will create a DLL and so gives you a Primary Output.

In my earlier post I explained how to add a Custom Dialog screen. You will need 2 parameters called PHYPATH and WEBSITEPORT. For Custom Actions add, and this is important, the Primary Output from WebSiteInstaller. The CustomActionData needs to be
For the Install Action

/phyPath="[PHYPATH]" /webSitePort="[WEBPORT]" /targetdir="[TARGETDIR]\
For the Uninstall Action

/targetdir="[TARGETDIR]\"
Last thing to do in this setup project is to add the two batch files.
Here is the content of the CREATEWEBSSITE.BAT. Note here my website name is WCFSaveFile which you will have to change. And you have to specify which files you want to copy to the target directory. In my case its an SVC and the web.config and the contents of the bin directory.

@echo OFF

set PHYPATH=%1
set PORT=%2
set SOURCEPATH=%3

set "FULLPATH=%PHYPATH%\WCFSaveFile"

md %FULLPATH%
md %FULLPATH%\bin

copy %SOURCEPATH%SaveFileService.svc" %FULLPATH%
copy %SOURCEPATH%web.config" %FULLPATH%
copy %SOURCEPATH%bin\*.*" %FULLPATH%\bin

REM Create Application Pool

%systemroot%\system32\inetsrv\APPCMD add apppool /name:WCFSaveFile
%systemroot%\system32\inetsrv\APPCMD set apppool "WCFSaveFile" /managedRuntimeVersion:v4.0
%systemroot%\system32\inetsrv\APPCMD set apppool "WCFSaveFile" /managedpipelineMode:Classic
%systemroot%\system32\inetsrv\APPCMD set apppool "WCFSaveFile" -processModel.identityType:NetworkService

REM Create WebSite

%systemroot%\system32\inetsrv\APPCMD add site /name:WCFSaveFile /bindings:"http/*:%PORT%:" /physicalPath:%FULLPATH%
%systemroot%\system32\inetsrv\APPCMD set app "WCFSaveFile/" /applicationPool:WCFSaveFile


The content of DELETEWEBSSITE.BAT is as follows.

@echo OFF

set PHYPATH=%1
set "FULLPATH=%PHYPATH%\WCFSaveFile"

REM Delete WebSite

%systemroot%\system32\inetsrv\APPCMD delete site /site.name:WCFSaveFile

REM Delete Application Pool

%systemroot%\system32\inetsrv\APPCMD delete apppool /apppool.name:WCFSaveFile

REM Delete virtual path

echo %FULLPATH%\bin\

del %FULLPATH%\bin\*.*" /Q
del %FULLPATH%\*.*" /Q
rd %FULLPATH%\bin
rd %FULLPATH%
Thats it. Your MSI should install the web site, create the App Pool and set it to .Net FW 4.0. I have just reused this for another web site and it took me only a few minutes to have a working MSI.
Hope you find it useful.
One last word of warning. NEVER add a final \ to the physical path when prompted by the MSI because it gives an obscure error.