Wednesday, January 30, 2013

Debugging Problem with Visual Studio 2012 Unit Test

Today I found a very annoying problem while debugging my unit test with Visual Studio 2012. In a usual case scenario, when you are debugging your assembly code, at any breakpoint you can hit F10 or F11 to step over or step into the code line by line. However, today I find it behave differently, when I step over my code, the execution is skip jumping a few lines or more.

I would like to share the steps how I troubleshoot this problem and found the root cause and resolution.

First, check all your projects are not running in "Optimize Code" by opening up project properties and look for Build section then locate the "Optimize Code" checkbox is not checked.



Then, run your projects in debug mode, open up the Module window from the Debug menu, and then check all your assemblies are loaded and identify all the symbol paths are correct.


The loaded symbol paths look fishy. When you go to the project bin folder, you would notice that there are extra pdb files in the folder. For example:

<Assembly Name>.dll
<Assembly Name>.pdb
<Assembly Name>.instr.pdb

And then, in your Module window, it shows that the loaded symbols are actually <Assembly Name>.instr.pdb.

Well, when we enable Code Coverage in unit test, the instrumented assembly will be generated into another set of *.pdb files which are named as <Assembly Name>.instr.pdb. I never notice the *.instr.pdb would actually get loaded instead.

After disabling the Code Coverage, Test Impact, Intelli Trace, everything from my unit test setting, then do a solution cleaning and rebuild, no more *.instr.pdb file is generated. While debugging the code, only normal *.pdb files get loaded and no more code jumping around after hitting F10.

However, I am a little bit curious what is the actual root cause, so I go and enable back the Code Coverage. Surprisingly, *.instr.pdb files are not generated. So, I go and enable Test Impact, now I see *.instr.pdb files are generated, and I replicated the same problem occur earlier again.

So, is it a bug in Visual Studio 2012? :P



Sunday, January 20, 2013

How to Configure WCF Service Client to Consume 3rd Party Web Service via Secure Channel

One of the developers in my company faced a problem in consuming a 3rd party web service from her ASP.net web application. The web service is hosted in a secure channel. The vendor provided a self-signed SSL certificate. I would like to share how to configure our WCF client to connect to the 3rd party web service via HTTPS with the provided self-signed SSL certificate.

First, we need to import the provided cert into the local machine.
  1. Open Management Console (mmc.exe)
  2. Click File menu, then click at Add or Remove Snap-ins.
  3. Select Certificates from the list box then click the Add button.
  4. Select Computer Account then click Next button.
  5. Select Local Computer then click Finish button.
  6. Collapse the Certificates tree view, right click the Personal folder, then select All Tasks, then click at Import...
  7. Select Local Machine, then click Next button.
  8. Click the Browse button and locate the certificate which provided by the vendor.
  9. Place the certificate to the Personal Certificate Store, then click the Next button.
  10. Finally, click the Finish button.


Then, go to your Visual Studio, add a web service reference to your project first with the provided WSDL by the vendor. Then, you will be warned that the site's cert is not issued by a company that you have not chosen to trust. This is a normal warning when the vendor does not use a cert which provided by a Certificate Authority.


After adding the service reference, the configuration file will be added with binding configuration automatically.

Ensure the binding security mode is Transport, then client credential type is Certificate.


<bindings>
  <basicHttpBinding>
    <binding name="<Binding Name>">
      <security mode="Transport">
        <transport clientCredentialType="Certificate" />
      </security>
    </binding>
  </basicHttpBinding>
</bindings>

Then, create a binding behavior that locate your certificate.

<behaviors>
  <endpointBehaviors>
    <behavior name="CertificateBehavior">
      <clientCredentials>
        <clientCertificate storeLocation="LocalMachine"
                            storeName="My"
                            findValue="bc74ca0d37510e5e87773707b60cd0490653662f"
                            x509FindType="FindByThumbprint" />
        <serviceCertificate>
          <sslCertificateAuthentication certificateValidationMode="None"/>
        </serviceCertificate>
      </clientCredentials>
    </behavior>
  </endpointBehaviors
</behaviors>


There are a few ways to locate your certificate. I chose to find by thumbprint. How to locate the certificate thumb print? Go back to the Management Console and then double click the imported certificate. Go to the details tab, scroll down and locate the thumbprint field.


Copy and paste the thumbprint to your configuration file, and then remove all the spaces.

If you wish to find certificate by issuer name, you can refer to the value in the Issued By in the General tab. And then, set findValue="CN=<issuer name>".

Finally, set the endpoint behavior configuration.

<endpoint address="https://<webservice url>"
  binding="basicHttpBinding" bindingConfiguration="<binding name>"
  contract="<contract name>" name="<endpoint name>"
  behaviorConfiguration="CertificateBehavior" />

When you do a test run, you may encounter some common errors.

Error 1: Could not establish trust relationship for the SSL/TLS secure channel with authority
Root Cause: Due to self-signed certificate, the client cannot validate the certificate.
Resolution: Disable client SSL certificate authentication with the following endpoint behavior configuration.

<serviceCertificate>
  <sslCertificateAuthentication certificateValidationMode="None"/>
</serviceCertificate>


Error 2: Cannot FindByThumbprint - Invalid Hexadecimal String Format
Root Cause: Cannot locate the certificate. Either the certificate thumbprint is invalid, or the certificate was imported into different user store.
Resolution: If you run your web application by using Visual Studio Development Server, the hosting service is running under your current login account. You need to import the cert to your current user and set the correct certificate store in your config file. When you deploy your application to IIS, you need to make sure the IIS service account is imported with the correct cert.



Workflow Versioning - Part 2

Continue from the previous post Workflow Versioning - Part 1.

Today I want to share how to host workflow with multiple version in IIS/WAS by using Windows Service Host Factory.

First, create a workflow and name it as v1 and one more different workflow as v2. For example, in my case I name it as VersionWFv1 and VersionWFv2.


Note: this is a different way of creating multiple workflow version compare to my previous post for the part 1. In part 1, I sign the assembly WorkflowVersioning.Workflows.dll into 2 different version but same assembly name. If you had followed the part 1 and created the 2 assemblies with different version, it is difficult to host them in IIS as I read some article found from the google search that we need to do custom service routing to locate the correct version of workflow service. And I found one article in MSDN, in .net 4.5, we save all the hassle and it is now very easy to have different version of workflow running side by side.

Today, I create 2 workflow xaml (class) with different name to differentiate the version. I find that when we sign a same assembly name with different versions, it is also difficult to trace back previous implementation since we had compiled it into dll. In today part 2, I create new class base on an existing class with different name, it is easier to look back the previous implementation, and also we can rollback changes easily.

Back to the topic, how to host 2 different workflow version with Windows Service Host Factory?

We cannot use the original Windows Service Host Factory because the existing implementation does not support service versioning with config file, also the service name is set base on the class name by default. We need to extend it to support hosting same service name but is reference from different classes. For my case, I would like to host the 2 different workflow with the same service name call "VersionWF" (see below yellow highlight), but 2 different classes (blue highlight) which tell the version difference.

The following sample code is sourced from Serena. We need the following assembly reference to extend the Workflow Service Host Factory.
System.Activities
System.ServiceModel
System.ServiceModel.Activities
System.ServiceModel.Activation


public class CustomServiceHostFactory : WorkflowServiceHostFactory
{
    protected override WorkflowServiceHost CreateWorkflowServiceHost(Activity activity, Uri[] baseAddresses)
    {
        // Current workflow service.
        WorkflowService current = new WorkflowService
        {
            Name = "VersionWF",
            Body = new VersionWFv2(),
            DefinitionIdentity = new WorkflowIdentity
            {
                Name = "VersionWF v2",
                Version = new Version(2, 0, 0, 0)
            }
        };

        // Older version.
        WorkflowService version1 = new WorkflowService
        {
            Name = "VersionWF",
            Body = new VersionWFv1()
        };

        // Create WorkflowServiceHost
        WorkflowServiceHost host =
            new WorkflowServiceHost(current, baseAddresses);
        host.SupportedVersions.Add(version1);

        return host;
    }
}


Take note that the workflow service version 1 does not have DefinitionIdentity. If you already had an existing instance in the persistence store, you cannot define the definition identity for version 1 because you never define the definition identity in the first time, if you define it now, you would get an error.

Now, we use the extended service host factory by defining it in the config file. Below is my complete configuration:


<system.serviceModel>
  <serviceHostingEnvironment multipleSiteBindingsEnabled="true">
    <serviceActivations>
      <add factory="WorkflowVersioning.Hosts.CustomServiceHostFactory"
                       relativeAddress="./VersionWF.svc" service="WorkflowVersioning.Workflows.VersionWF"/>
         </serviceActivations>
  </serviceHostingEnvironment>
  <services>
    <service name="VersionWF"
    behaviorConfiguration="WorkflowServiceBehavior">

      <endpoint name="basicHttpWorkflowService"
          address=""
          binding="basicHttpBinding"
          contract="IService" />

      <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />

    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="WorkflowServiceBehavior">
        <serviceMetadata httpGetEnabled="true" />
        <serviceDebug includeExceptionDetailInFaults="true" />
        <serviceTimeouts transactionTimeout="00:10:00"/>
        <serviceThrottling maxConcurrentCalls="1000000"
                            maxConcurrentInstances="1000000"
                            maxConcurrentSessions="1000000" />
        <sqlWorkflowInstanceStore connectionStringName="WorkflowInstanceStore"
          hostLockRenewalPeriod="00:00:30" runnableInstancesDetectionPeriod="00:00:05"
          instanceEncodingOption="GZip" instanceCompletionAction="DeleteNothing"
          instanceLockedExceptionAction="AggressiveRetry" />
        <dataContractSerializer maxItemsInObjectGraph="2147483647" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>


That's all about it. Here are the source if you wish to read more about it: MSDN






Send Transactional SMS with API

This post cover how to send transactional SMS using the Alibaba Cloud Short Message Service API. Transactional SMS usually come with One Tim...