Programming‎ > ‎

WCF

WCF Programming Tips

I started WCF programming using C++/CLI and now I use C#. I encountered several "gotcha"'s.  I like to list them here. The concept of WCF is interesting in that you separate writing code (DLL) and employing code (web.config or app.config). You can use different binding at runtime by modifying configuration.

Updated 6/26/2011

Index


  • How can I write the serialized object to a file?
  • DataContractSerializer takes Stream as the argument. Here is how.

      
      Funny f = new Funny();  
      DataContractSerializer dcs = new DataContractSerializer(typeof(Funny));  
      // writing a serialized object to a file  
      using (FileStream fs = new FileStream(@"test.xml", FileMode.Create))  
      {    
        dcs.WriteObject(fs, f);  
      }  
      // reading a serialized object from a file  
      using (FileStream fs2 = new FileStream(@"test.xml", FileMode.Open))  
      {    
        object obj = dcs.ReadObject(fs2);    
        Funny f2 = obj as Funny;  
      } 
      

    Top


    How can I serialize an object containing an interface?

    For example, I have a class MyTrip which contains IAircraft object where Airplane and Helicopterare concrete classes of IAircraft. In order to serialize/deserialize this MyTrip object, you need to add the KnownType attribute of concrete classes of an interface to MyTrip declaration or DataContractSerializer to have KnownType list as the second argument.

       
      [DataContract]   
      [KnownType(typeof(Airplane))]   
      [KnownType(typeof(Helicoptor))]   
      public class MyTrip   
      {     
        [DataMember]     
    	private IAircraft aircraft;   
      }
      
    Read http://msdn.microsoft.com/en-us/library/ms730167.aspx. KnownType is needed only for DataContractSerializer.BinaryFormatter and NetDataContractSerializer will do the discovery by themselves and record the type inserialized object as long as the class is marked as [Serializable].

    Top


    Can you tell how ICollection object is transfered to the destination?

    As a default, WCF uses DataContractSerializer. The question came up what we should do with List object in a class. Should we do ICollection or IList or List in the WCF interface. It turned out that Collection object is always transferred as an array over the wire.(page 76 of "Essential Windows Communication Foundation For .NET Framework 3.5" by S. Resnick et al.). You can see this by serializing a class which contains ICollection object. The resulting XML file serialized shows the object being as an array:< array xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays" >. The bigger question is what the object looks like when deserialized at the destination. The result is thatICollection or IList object becomes an array object. Contrary to this, List object stays asList object.   This can be seen by using GetType() of the deserialized object.  Interesting thing is that when you use BinaryFormatter or NetDataContractSerializer, then the original object type is preserved, i.e. even when you have WCF interface ICollection acol, you assign acol = List object, then the destination will have acol being List object. It turned out that these serializer records the actual object type in the serialized object even though the transfer is done using an array.

    Top


    How can I fix the error "The remote server returned an error:(400) Bad Request"?

    The WCF configuration as default uses the following small size:MaxBufferPoolSize = 524288, MaxReceivedMessageSize = 65536, ReaderQuotas.MaxArrayLength = 0,MaxBytesPerRead = 0, MaxDepth = 0, MaxNameTableCharCount = 0, MaxStringContentLength = 0. That is why I get Bad Request, since I was sending 80Kbytes! (when I send 5 bytes, it worked). You can use WCF Service Configuration Editor in Visual Studio Tools menu (once you open this, it is available when you right click app.config). While developing my WCF server, the debugging session did not use the updated configuration and thus I had to hard-code the values as follows (I'm using WSHttpBinding):

      
      using (ServiceHost svh = new ServiceHost(typeof(ContourClosure)))  
      {    
        WSHttpBinding binding = (System.ServiceModel.WSHttpBinding)svh.Description.Endpoints[0].Binding;    
    	binding.MaxReceivedMessageSize = 50000000;    
    	binding.MaxBufferPoolSize = 50000000;    
    	binding.ReaderQuotas.MaxArrayLength = 50000000;    
    	binding.ReaderQuotas.MaxBytesPerRead = 50000000;    
    	binding.ReaderQuotas.MaxDepth = 50000000;    
    	binding.ReaderQuotas.MaxNameTableCharCount = 50000000;    
    	binding.ReaderQuotas.MaxStringContentLength = 50000000;    
    	svh.Open();    
    	...  
      }
      

    Top


    How can I resolve the error "WCF service reference namespace differs from original"?

    When my client application tries to create a proxy class using "Add Service Reference", the warning is generated which says WCF service reference namespace differs from original and suggests to add explicit namespace mappings. Huh? Worse, the interface I'm looking for is notavailable in the proxy class. This is explained in http://stackoverflow.com/questions/1200346/wcf-service-reference-namespace-differs-from-original. The process to fix this is cumbersome. First enable "Show All Files" optionin Solution Explorer (the second toolbar button) and then double clicking Reference.svcmap file. You find <NamespaceMappings/>. You expand it to have <NamespaceMapping> and add TargetNamespace="..." and ClrNamespace="..." where ... is to copy the exact namespacewarning message says. Save the file and then "Update Service Reference" (make sure that theservice is still running). Now I have the proxy class to the service I want.

    As you found out, the method above is really ugly.  It turned out that this was caused by the client application lacking the references to those whose service reference namespace differering named.  Once I add those reference libraries to the client project, this message went away.   This is certainly the easiest way to fix this error.

    Top


    Why WCF connection refused on P2P network?

    On P2P network, the following must be done.

    
      1. Simple File Sharing must be turned off.  Under Control Panel → Folder Options   → View, 
         scroll to the bottom of the "Advanced Settings" list and uncheck the box labeled "Use simple file sharing" 
    	 (under WinXP), 
      2. There must be a common user account with the same password.  In other words, it must be possible 
         to log on to all machines using the same User ID/password.
      

    The reason for 1 is that when set, the user account is mapped to "guest" account on the remote machine. See http://connect.microsoft.com/wcf/feedback/details/289139/windows-authentication-fails-on-windows-xp-sp2-without-domain-workgroup.

    Top


    How can I serialize a type which has circular references?

    Thanks to Zulficar, having [DataContract(IsReference=true)] will enable serialization of object graphs having circular references (which wasn’t possible previously – at least not without writing custom code) and will also reduce the size of the serialized xml.

    Top


    How can I configure two bindings for WCF?

    One of the benefit of WCF is that you can expose different bindings (can you name them all? Here is the link). Unfortunately most of the examples found use only one. Here is the example to have two bindings in the server configuration. The point is that you have to have three things:
    1. give different port numbers,
    2. use appropriate MetadataExchange classes for bindings,
    3. give different endpoint names for bindings.
    When you create a client, make sure to name the endpoint name for a particular binding. Otherwise, you get throw.

      
      <services>     
         <service behaviorConfiguration="StockServiceBehavior" name="BehaviorTest.StockService" >        
            <host>           
              <baseAddresses>              
              <add baseAddress="http://localhost:8080/BehaviorTest/"/>              
              <add baseAddress="net.tcp://localhost:8088/BehaviorTest/"/&t;           
              </baseAddresses>        
            </host>        
            <endpoint address="" binding="basicHttpBinding" 
                    name="BehaviorTest" contract="BehaviorTest.IStockService" />        
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
            <endpoint address="" binding="netTcpBinding" name="BehaviorTestTcp" contract="BehaviorTest.IStockService" />
            <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
        </service>
      </services>
      

    Top


    How can I fix the error "Service has zero application" ?

    Just using the wizard created App.config file for the WCF class library, copying it to Console host, it produced the error:
    Service 'ReqResService.StockService' has zero application (non-infrastructure) endpoints.
    This might be because no configuration file was found for your applicalication, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element.

    I was totally confused by the error. It turned out that Microsoft is confused with just one thing. I forgot to modify service name in App.config!

    Top


    How can I verify working of a WCF component?

    Make sure that MEX (Metadata Exchange) endpoint is exposed in the configuration (App.config or web.config) so that you can see the metadata exposed by a WCF component. In particular, a mex endpoint over HTTP allows you to use a browser to check.  Don't forget to set the security permission on the hosting address (see How can I set permission for WCF component?).

    
      <!-- add behaviorConfiguration -->   
      <services>     
        <service behaviorConfiguration="mybehavior" 
    	...        
          <host>
             <baseAddresses>            
                <add baseAddress="http://localhost:8080/BehaviorTest/" />
             </baseAddresses>              
          </host>        
          <endpoint name="BehaviorTest" binding="basicHttpBinding" contract="..." >
             <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" >
        </service>   
      <services>
      <!-- add behavior for MEX -->   
      <behaviors>
        <serviceBehaviors>
          <behavior name="mybehavior">
            <serviceMetadata httpGetEnabled="true" >
    	    <serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true"/>
    	  </behavior>
        </serviceBehaviors>
      </behaviors>
      

    Note that it uses HTTP-GET. If the service has not set up HTTP baseAddress (the address is specified like <service address="http:..." >), then it will throw. In such a case, you add httpGetUrl attribute like

             
      <behavior name="MEX">            
        <serviceMetadata httpGetEnabled="true" httpGetUrl="(address for service)" >         
      </behavior>
      
    Once you have this mex configuration, you confirm your service hosted by an applicatin by (address for service)?wsdl, e.g. http://localhost:8080/BehaviorTest/?wsdl. (IE8 shows the wsdl. Firefox v.3.6.3 complains about "This XML file does not appear to have any style information associated with it. The document tree is show below".) If your component is hosted in IIS, you can identify problems by doing http://localhost:(portnumber)/MyService/MyService.svc. When you get the standard "You have created a service" message web page, then at least IIS was able to instantiate the component. The utility svcutil.exe should do the same task for non IIS-hosted components. If your component is hosted in a webservice or .exe, then you use the address of the component for the address in the browser. You will get the default web page for a web service which contains the link for wsdl page (Web Service Description Language). If you have problems with this page, then your WCF component has problems.

    Top


    What is this error "unrecognized element 'extendedProtectionPolicy'?

    When you add Service Reference to the WCF client under VS2008, it adds the extendedProtection policy tag under security in App.config. This is the reason for the uncaught exception. It turned out that this tag is available only under Silverlight 3. You can comment out that line safely.

    Top


    WCF configuration files are not checked by the compiler

    Make sure that where to put namespace, interface name, and concrete class name in the configuration file correctly. These are in text format. Compiler won't check. If you make typos in these, your component cannot be used and spend your precious moments wondering why it does not work. For example, Servcice.svc file contains

    
      <%@ServiceHost Service="(Namespace).(Concrete class name)" %>
      
    On the other hand, web.config needs both concrete class name and interface name be put down in different places:
    
      <services> 
        <service name="(Namespace).(Concrete class name)">
          <endpoint ... contract="(Namespace).(Interface name)">
        </service>
      </services>
      

    Top


    How can I write to eventlog from my WCF component?

    When you want to write event logs under your component, you must first create Event Source ( you need the admin priviledge to do this). Otherwise you get a security exception (System.Security.SecurityException: Requested registry access is not allowed) under Vista or Windows Server 2008. This is explained in http://support.microsoft.com/?id=329291/. You create a EventLog source installer class dll in the following way:

    
      using System;
      using System.Collections.Generic;
      using System.Text;
      using System.ComponentModel;
      using System.Diagnostics; 
      // EventLogInstaller
      using System.Configuration.Install;
      // Use this with installutil 
      //// installUtil EventLogInstaller.dll//// 
      // change source and log name//
      namespace EventLogSourceInstaller
      {  
        [RunInstaller(true)]  
    	public class MyEventLogInstaller : Installer  
    	{    
    	  private EventLogInstaller MyServiceLogInstaller;    
    	  public MyEventLogInstaller()    
    	  {      
    	    ///////////////////////////////////////////////      
    		//Create Instance of EventLogInstaller      
    		MyServiceLogInstaller = new EventLogInstaller();      
    		// Set the Source of Event Log, to be created.      
    		MyServiceLogInstaller.Source = "MyService";      
    		// Set the Log that source is created in      
    		MyServiceLogInstaller.Log = "MyServiceLog";      
    		Installers.Add(ReticaEMServiceLogInstaller);    
    	  }  
    	}
      }
      

    Top


    How can I use C++ DLL in my WCF component? It seems that it is not found at runtime.

    IIS hosted WCF case

    When a WCF component hosted in IIS is instantiated, the module path is set to c:\Windows\Microsoft.NET\Framework\v2.0.50727\ and the current dir is set to c:\Windows\System32. When you write WCF components with C++/CLI using C++ libraries, you must first do delay-load C++ libraries under your WCF components. Otherwise the WCF component instantiation fails. You get exception saying the necessary components not found.

    Next the constructor of your component must set current directory to where your C++ libraries are stored:

      using namespace System::IO;
      using namespace System::Web;
      ... 
      (inside the constructor of your WCF component)
      String^ binPath = System::Web::Hosting::HostingEnvironment::MapPath("~/bin/");
      System::IO::Directory::SetCurrentDirectory(binPath);
      
    The example above is due to the fact that I usually stores C++ DLLs in the same directory as WCF DLL is stored. Note that MapPath() returns the path with respect to the physical root path of IIS if you don't use "~/". This behavior is different from inside the Web Application where you get the application path by using "bin". It seems that changing the environment variable "PATH" won't work. Note also that the following standard way to use Server variable won't work inside the WCF component.
      
      HttpServerUtility^ su = System::Web::HttpContext::Current->Server;  
      String^ binPath2 = su->MapPath("bin");
      

    Web Service hosted case

    Do the following way.

    
      using System::Windows::Forms;
      ...  
      Directory::SetCurrentDirectory(Application::StartupPath);
      

    Top


    How can I eliminate tempuri.org from the WCF wsdl description?

    It is really annoying to see http://tempuri.org/ showing up under Service?wsdl command. There are at least three places you encounter.

    
      Root WSDL 
      <wsdl:definitions name="MyService" targetNamespace="http://tempuri.org/" />
      Put Namespace attribute to the concrete class:
      [ServiceBehavior(Namespace="http://www.tosa.com/services/")]
      public class MyService: IMyService
      {    
      ...  
      }
      wsdl:import 
      <wsdl:import namespace="http://tempuri.org/" location="http://udon/MySewrvice/Service.svc?wsdl=wsdl0" />
      Put binding Namespace in mex portion of Web.config or app.config:
      <endpoint address="mex" contract="IMetadataExchange" binding="mexHttpBinding" 
      bindingNamespace="http://www.tosa.com/services" />
    xsd:schema To specify the right namespace use ServiceContract annotation on contract interface. When you add this, you have to update the service reference. [ServiceContract(Namespace="www.tosa.com")] public interface IMyService { ...

    Top


    How can I run a 32bit WCF component under 64 bit Server?

    In order to pass Microsoft Certified Partiner conidition, I had to make my 32 bit WCF component (it is calling a 32 bit C++ DLLs) to run under Windows Server 2008. The WCF component was hosted under IIS 7.0. The way to do is to create a IIS 7.0 Application Pool. Here is the step.

    
      1. Click Start, click Administrative Tools, and then run IIS Manager.
      2. On the left pane, select Application Pool.  The following shows the     
         default application pools.
    	 
    	   
      3. Click Add Application Pool   I named 32bitAppPool. 
             
    	   
      4. Click on the newly created 32bitAppPool and click on Advanced Setting.
         Select "Enable 32bit Application".
    	     
      5. WebApp setup asks user about Application Pool.  Tell user not to pick DefaultAppPool      
         but 32bitAppPool.
    	 

    Top


    How can I avoid the error 'This element is not currently associated with any conttext' ?

    Under VS2008 IDE when trying to run a WCF web client, you will get 'This element is not currently associated with any conttext'. This is caused by WCF throws (but caught exception) under normal operation. No fix at this time. Fix is not to break.

    
      VS2008->Menu->Debug->Exceptions 
      ...
      Uncheck Common Language Runtime Exceptions -> SystemConfiguration->System.Configuration.ConfigurationException
      

    See http://social.msdn.microsoft.com/forums/en-US/wcf/thread/2620084b-a77b-4c77-b9ce-725fbea1df55/

    Top


    How can I write WCF component under VS2005?

    First you install .NET 3.5 Sp1. Microsoft keeps changing the content of the download and thus it is safe to go to the Microsoft website for .NET installation. This will automatically installs the necessary components to write WCF (it installs .NET 2.0 SP2, 3.0, and 3.5 SP1). You also need to install the latest Windows SDK (don't bothered by 2008 or Vista) which will provide SvcUtil.exe, SvcConfigEditor.exe, SvcTraceViewer.exe. SDK installs on Windows XP.

    Microsoft created a Visual Studio 2005 extension for WCF/WPF called vsextwfx.msi. Unfortunately when you install .NET 3.5 SP1, it will no longer be installed, claiming that .NET 3.0 is not found. (This component is no longer available from Microsoft. Download it from the trustable website.) Here is the how-to-install.

      
      msiexec /i vsextwfx.msi WRC_INSTALLED_OVERRIDE=1
      

    Just ignore the startup message saying .NET 3.0 is needed. Got info from here.

    By the way, here is the command line options for msiexec found in a simple version here and a detailed version here

    Top


    How to remove a weird error Error connecting to undo manager of source file pagename.aspx.designer.cs under VS2005

    Here is how.

      
      1. Shut down Visual Studio  
      2. Reset IIS or kill the asp.net worker process.  
      3. Go to :\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files (in WinXP) and delete relevant files.   
      4. Restart your project and then rebuild.
      

    Top


    How to add .NET 3.5 as prerequisite for setup under VS2005?

    .

    Under VS2005, web setup project uses files from c:\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages. U nfortunately NETFX30 directory is broken in that files referred in product.xml are missing, i.e. dotnetfx3setup.exe, dotnetfx3_x64.exe, dotnetfx3.exe are missing. Fortunately if you have installed Windows SDK v6.0A, then you have the complete package for .NET 3.5 available. You just copy the directory c:\Program Files\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Packages\DotNetFX35 into the directory above, then the setup utility will recognizes it and add the option of prerequisites for .NET Framework 3.5. Modify product.xml for .

    Top


    How can I set permission for WCF component?

    The security under windows is more scrutinized and thus you have to set permission for the port you use for WCF. The way you do it is different for XP, Vista, and Win7. Here is the description. It turned out that this web site mentions httpcfg for XP but it is not available and at least I do not need to set. However, it is required for Windows 7, otherwise you cannot run the WCF server.

    
      Under Windows7 (use port 8000) 
      $ netsh http add urlacl url=http://+:8000/HelloIndigo user=somen\ytosa 
      URL reservation successfully added 
      Removing is done by 
      $ netsh http delete urlacl url=http://+:8000/HelloIndigo
      

    The second line is the response after hitting Return.

    Top


    How can I avoid System.BadImageFormatException?

    I'm now running under Windows7 64 bit. The WCF client program used to run under Windows XP now gets "System.BadImageFormatException". My client app calls a WCF component which calls the 32 bit C++ DLL which caused this problem, since the C++/CLI was set to compile for "Any CPU" mode. With this setting, it will try to call native 64 bit DLL which is the reason for the error message. By changing "Any CPU" to "x86" for compilation mode, it runs now (since the program now runs under WOW64 mode).

    Top

    Home

    Â