Web test config file

If you tried to change your web test configuration settings by editing app.config, you discovered that it had no effect. That is because the web test runs inside Visual Studio Test Host, so you must edit C:Program FilesMicrosoft Visual Studio 9.0Common7IDEVSTestHost.exe.config instead.
Advertisements

Can’t find PInvoke DLL ‘sqlceme35.dll’

I was working with a Windows mobile application that uses SQL Server Compact. After upgrading to Visual Studio 2008 SP1 and SQL Server Comapct 3.5 SP1 I got MissingMethodException, Can’t find PInvoke DLL ‘sqlceme35.dll’. The problem is that the native DLL is not installed. I tried various solutions to no avail, and the only one that worked for me was to install the missing DLL manually. It is included in a CAB file located under C:\Program Files\Microsoft SQL Server Compact Edition\v3.5\Devices\<platform>\<processor>, but which platform and processor is the right one? I found the answer here: http://forums.microsoft.com/technet/showpost.aspx?postid=2594381&siteid=17. All Windows Mobile 5 and above devices are using sqlce.phone.wce5.armv4i.cab, so that’s the one you need to install. (So platform is wce500 and processor is armv4i.)
Configure a shared folder in the emulator and copy the cab there. On the emulator device, open the cab and it installs.

Correlation between orchestration and ports

Richard Hallgren wrote a good post about Using BAM for latency tracking in a BizTalk request response scenario. (It contains one error though – you should use InstanceId, not ServiceID as continuation. See another post for more details.)
 
But what if you also want to track things in an orchestration and correlate that with port tracking? What you have to do is to create two continuations. One takes care of the request/respons correlation as Richard describes, and the other correlates that with the orchestration. This continuation uses InterchangeID. Step-by-step:
 
1. Add a second continuation and call it e.g. OrchestrationToPort.
2. Click Select Event Source, Select Orchestration Schedule, find your orchestration, right click on the send shape and select Message Property Schema. Select InterchangeID and drag it to the OrchestrationToPort continuation. Possibly, do this for more orchestrations you’re interested in tracking.
3. Create a continuation ID and call it the same name as the continuation.
4. Click Select Event Source, Select Messaging Property, select InterchangeID and drag it to your OrchestrationToPort continuation ID. Right click your newly added InterchangeID on the left side of the TPE and select Select Port Mappings. Select the send port (or ports).
 

Calling web services diretcly from orchestration

I worked with a customer that made heavy use of synchronous web services and BizTalk. They had receive ports that accepted web service calls, and these ports were bound to orchestrations that contained the logic. The orchestrations aslo called back-end web services. The problem with this approach is that BizTalk is not a very good fit with low-latency requirements. A certain overhead is added to each web service call, which is routed through the message box. So we decided to call a couple of heavily used web services directly from orchestration using a .net helper class. The problem was now how to write this helper. I didn’t want to generate a typed proxy the usual way (using add web service reference or using svcutil.exe), because the BizTalk solution already contained the request and response schemas (the development was using contract first) and messages of these schemas. If we had used a typed proxy, the XML message had been turned into .net objects, which had been serialized to XML again to send the request, and vice versa for the respons. And if the schema (contract) is changed, the proxy must be regenerated.
 
The solution was to create a client that inherits from ClientBase<IRequestChannel>, and to use the Message class to create the request. Here is the complete code:
 
    public class ServiceProxy : ClientBase<IRequestChannel>
    {
        /// <summary>
        /// Calls an external web service using WCF.
        /// </summary>
        /// <param name="port">"Port" name. This is used to lookup endpoint address and action in ESSO.</param>
        /// <param name="xlangMessage">The request message to send.</param>
        /// <returns>A message consisting of the response, if successful. If a fault is returned from the service, an exception is thrown.</returns>
        public static XmlDocument Call(string port, XLANGMessage xlangMessage)
        {
            try
            {
                // To use SSOConfigHelper, you must create one or more SSO application and add properties to it.
                // 1. Use SSOManage to create the application:
                // "C:Program FilesCommon FilesEnterprise Single Sign-Onssomanage.exe" -deleteapp MyApp
                // "C:Program FilesCommon FilesEnterprise Single Sign-Onssomanage.exe" -createapps "MyApp.xml"
                // 2. Set property values:
                // "C:Program FilesMicrosoft BizTalk Server 2006SDKScenariosCommonSSOApplicationConfigbinBTSScnSSOApplicationConfig.exe" -set MyApp ConfigProperties MyPortAddress http://aServer/MyService.svc
                // "C:Program FilesMicrosoft BizTalk Server 2006SDKScenariosCommonSSOApplicationConfigbinBTSScnSSOApplicationConfig.exe" -set MyApp ConfigProperties MyPortAction MyAction

                BasicHttpBinding binding = new BasicHttpBinding();
                binding.MaxReceivedMessageSize = 5 * 1024 * 1024; // 5 MB
                // Check if a proxy address is configured.
                string proxyAddress = SSOConfigHelper.Read("ProxyAddress");
                if (proxyAddress != null && proxyAddress != "")
                {
                    binding.UseDefaultWebProxy = false;
                    binding.ProxyAddress = new System.Uri(proxyAddress);
                }
                string addressConfig = port + "Address";
                string actionConfig = port + "Action";
                string endpointAddress = SSOConfigHelper.Read(addressConfig);
                string action = SSOConfigHelper.Read(actionConfig);
                TraceLog.Write(string.Format("Configuration read from SSO: {0}='{1}', {2}='{3}'", addressConfig, endpointAddress, actionConfig, action), "", false, typeof(ServiceProxy).FullName, "Call");
                if (endpointAddress != null && action != null)
                {
                    ServiceProxy client = new ServiceProxy(binding, new EndpointAddress(endpointAddress));
                    return client.CallInternal(action, xlangMessage);
                }
                else
                {
                    throw new Exception(string.Format("Invalid SSO configuration: {0}={1}, {2}={3}", addressConfig, endpointAddress, actionConfig, action));
                }
            }
            catch (MyException)
            {
                // Just rethrow, since this exception has already been logged.
                throw;
            }
            catch (Exception ex)
            {
                ErrorLog.Write(ex, "", "", typeof(ServiceProxy).FullName, "Call");
                throw;
            }
            finally
            {
                // Always call dispose, because XLANGMessages are reference counted.
                // (http://www.masteringbiztalk.com/blogs/jon/PermaLink,guid,591a086d-59b7-4140-a327-a87091ec8330.aspx)
                xlangMessage.Dispose();
            }
        }

        private ServiceProxy(Binding binding, EndpointAddress endpointAddress)
            : base(binding, endpointAddress)
        {
        }

        private XmlDocument CallInternal(string action, XLANGMessage xlangMessage)
        {
            TraceLog.Write("Request", "", "", false, GetType().FullName, "CallInternal");
            XmlReader reader = (XmlReader)xlangMessage[0].RetrieveAs(typeof(XmlReader));
            Message req = Message.CreateMessage(MessageVersion.Soap11, action, reader);
            Message reply = Channel.Request(req);
            XmlDocument result = new XmlDocument();
            result.Load(reply.GetReaderAtBodyContents());
            TraceLog.Write("Response", result.InnerXml, "", true, GetType().FullName, "CallInternal");
            bool ok = !reply.IsFault; 
            req.Close();
            reply.Close();
            if (ok)
            {
                return result;
            }
            else
            {
                throw ContstructException(result);
            }
        }