Friday, February 22, 2008

I've been putting together a small WCF service that allows a client to upload a file to a server for processing.  While the details of that are for another discussion, I want to focus on what should have been a simple operation but turned out to take up most of my afternoon and some of my evening: the generation of a client proxy class and configuration file.

By way of background, this particular WCF service is hosted by a simple Windows Service, runs (by default) under the Network Service account, and exposes a TCP end point for communication.  I have a setup project that takes care of the installation and registration of the Windows Service and auto-runs it when installation is complete.  This has been running great.

However, anytime I attempted to generate a proxy class for my service via the following command line, I would get some nasy error indicating that it “Cannot obtain Metadata from net.tcp://localhost:9999/myservice”.

svcutil net.tcp://localhost:9999/myservice/mex

My configuration file was as follows:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   <system.serviceModel>
      <behaviors>
         <serviceBehaviors>
            <behavior name="mex">
               <serviceMetadata />
            </behavior>
         </serviceBehaviors>
      </behaviors>
      <services>
         <service name="MyService.ExampleSvc" behaviorConfiguration="mex">
            <host>
               <baseAddresses>
                  <add baseAddress="net.tcp://localhost:9999/myservice" />
               </baseAddresses>
            </host>
           
            <!-- default endpoint -->
            <endpoint
               binding="netTcpBinding"
               contract="MyService.IExampleSvc" />
           
            <!-- metadata exchange (MEX) endpoint -->
            <endpoint
               address="mex"
               binding="mexTcpBinding"
               name="MEX"
               contract="IMetadataExchange" />
         </service>
      </services>
   </system.serviceModel>
</configuration>

However, as I mentioned, everytime I attempted to retrieve the metadata via the /mex endpoint it failed.  Right off the bat I suspected that the error had to do with the types used in the [ServiceContract] class, but I brushed that idea aside thinking that for sure it was something in the configuration file.  So I kept on battling with it.

To that end, I created a very simple WCF service (in the same application) this time with an HTTP endpoint.  I had to change the Windows Service credentials to a user with elevated permissions to register the HTTP channel (Network Service won't cut it) because I didn't want to manually mess with my HTTP security settings.  Anyway, this was just a test.  To my chagrin, it worked first try; I was able to retrieve the metadata.

So I returned to the TCP service.

Fortunately (and I'm no WCF whiz...yet :), I found a helpful behavior that I could add to the .config file that saved the day for me.  By simply adding the <serviceDebug /> element with the includeExceptionDetailInFaults attribute to the mex behavior element I was able to get some diagnostics.

<serviceBehaviors>
    <behavior name="mex">
       <serviceDebug includeExceptionDetailInFaults="true" />
       <serviceMetadata />
    </behavior>
</serviceBehaviors>

Now, in the big, fat, honkin' exception stack that was output I saw immediately that one of the embedded types used in my [ServiceContract] implementation had a nested type that was not serializable.  Once I added [Serializable()] to the offending types (there were a few of them) and recompiled it worked like a champ!  So it turns out that my initial suspiscion was correct...I need to listen to my intuition more often!

Friday, February 22, 2008 1:19:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, February 20, 2008

I ran into a peculiar behavior that Visual Studio 2008 was exhibiting today that I suspect is a bug.  If it's not a bug, then there must be a setting somewhere of which I'm not aware.

I have a couple of projects that I've developed initially in VS2003 (targeting the .NET 1.1 framework), maintained in VS2005 (targeting the .NET 2.0 framework), and recently upgraded to VS2008 (targeting the .NET 3.x framework).  These projects, rather than embedding image resources natively (that is, embedding the .png, .gif, and .jpg files as independent resources) I have some .resources files that contain string and image data.  Up to this point they've worked great.

However, I discovered today that once upgraded to VS2008 the resources were not loading.  My code for loading the resources is pretty simple:

ResourceManager mgr = new ResourceManager("MyApp.Res", Assembly.GetExecutingAssembly());
ResourceSet res = mgr.GetResourceSet(CultureInfo.InvariantCulture, true, false);

Note that my embedded resource file is named 'Res.resources', so the namespace for the ResourceManager is my application namespace followed by 'Res' because it's in the root of the application.

Now, with VS2008, the mgr.GetResourceSet() call was returning a NULL value.  Somehow my resources were not resolving! After digging a bit, I found that the '.resources' extension was being truncated off of my resource file during the embedding process so though it was present in the .exe, it's name didn't have the '.resources' extension appended!

My solution was to rename the file 'Res.resources.resources' and it worked.  Interestingly, the compiler doesn't exhibit this same behavior for the .resources files that it creates.  Has anyone else run into this or is there a setting for it?  Fortunately, I only lost about 15 minutes from discovering the issue to having it fixed, but it really feels like a hack more than a fix.

Wednesday, February 20, 2008 3:50:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [3]  |  Trackback
 Tuesday, February 12, 2008

It is once again time for our very own Utah .NET User Group monthly meeting! In fact, in lieu of this Thursday being Valentine's Day, we've bumped the meeting date up to Tuesday (TODAY - 02/12/2008), same time, same place. We realize this is short notice (shame on us). We posted the meeting on the calendar several days ago, but failed to send out the email reminder until now for which we apologize.

This month, grab your canoes, oars, and life preservers and get ready to talk about STREAMS. Streams, though fundamental and routinely used, come in many different flavors. We'll be exploring streams from the ground up; from the ever-so-commonly utilized MemoryStream and FileStream types to the not-so-frequently used CryptoStream. Hey, we may even venture into the realm of the NetworkStream, the NamedPipeServerStream and NamedPipeClientStream. Time permitting, we will delve into the very cool compression streams (such as DEFLATE and GZIP).

Time: 6:00 PM
Date: Tuesday, February 12, 2008
Place: Digital Draw Network (10897 South River Front Parkway, South Jordan) Suite 300

This month's meeting is generously sponsored by TEKsystems.

Come out and enjoy the food, the friendship, and the fun!

Tuesday, February 12, 2008 3:00:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, February 07, 2008

If you ever incorporate XmlSerialization into your applications, you're probably aware of the SGEN.exe utility.  If not, you should become familiar with it.  Without going into details about it, suffice it to say that by using sgen.exe you can greatly improve the performance of your applications that rely on serialization (e.g., Web Service clients, et al).  Without it, your applications suffer in part because the .NET Framework generates a dynamic assemblies at runtime for the Xml-Serializable types in your application when first serialized.  Note that this hit is taken 'up front' and doesn't affect the runtime performance as much as the initialization performance of your application.

Well, since VS 2005 MS has included an option within the GUI on the Build tab called “Generate serialization assembly” with three options: Auto, On, and Off.  Unfortunately, though the intentions were there, this option is largely unusable for several reasons.  The same problems have carried over to VS 2008 with no changes.  On the bright side, however, there are other options at our disposal.

  1. Use the sgen.exe from the command line.  Fun an exciting indeed!
  2. Set up a Post-build event command line referencing sgen.exe.  Even more exciting!
  3. The best option is to use the <SGen> MSBuild task which automates the process for us.  Awesome!

In fact, the main reason to use the <SGen> MSBuild task is to turn off the /proxytypes switch that the GUI throws in for us when the “Generate serialization assembly” is set to “On” which only has any meaning for Web Services.  Edit your .csproj file, dropping the following XML:

<Target Name="GenSerializationAssembly"
        DependsOnTargets="AssignTargetPaths;Compile;ResolveKeySource"
        Inputs="$(MSBuildAllProjects);@(IntermediateAssembly)"
        Outputs="$(OutputPath)$(_SGenDllName)">
  <SGen BuildAssemblyName="$(TargetFileName)"
        BuildAssemblyPath="$(OutputPath)"
        References="@(ReferencePath)"
        ShouldGenerateSerializer="true"
        UseProxyTypes="false"
        KeyContainer="$(KeyContainerName)"
        KeyFile="$(KeyOriginatorFile)"
        DelaySign="$(DelaySign)"
        ToolPath="$(SGenToolPath)">
    <Output TaskParameter="SerializationAssembly" ItemName="SerializationAssembly" />
  </SGen>
</Target>
<Target Name="AfterBuild" DependsOnTargets="GenSerializationAssembly" />

Now, when you build your project you'll get another DLL  with the name “assemblyname.XmlSerializers.dll”.  Simply deploy this DLL with your application to take advantage of the pre-generated serialization library. :-)

Note:  Thanks to Kiwidude for the pointer on the MSBuild task.

Thursday, February 07, 2008 3:47:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback