Tuesday, November 30, 2010

ContextSwitchDeadlock Exception

This ran into this strange exception which I haven't seen before.

The CLR has been unable to transition from COM context 0x33c5458 to COM context 0x33c55c8 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.
More on this exception and how to disable it from being thrown can be found here http://msdn.microsoft.com/en-us/library/ms172233(v=VS.90).aspx  and here http://blogs.microsoft.co.il/blogs/adlaim/archive/2007/08/06/The-CLR-has-been-unable-to-transition-from-COM-context.aspx

In my case, I believe the conditions that led to this exception to be thrown were caused by a call to a web service that was taking too (long > 60) seconds to either return or timeout.

Monday, November 1, 2010

Consuming Reporting Services 2005 Web Service

Reporting Service 2005 Web Service is a very useful API that lets do many tasks programmatically  such as rendering reports, uploading them, creating folders, setting properties etc.

In here I will try to show a brief tutorial on how to write a simple client that uses the SSRS 2005 Web Services. I assume you're using VS2008. The main point of this post is that when working Reporting Services 2005, you have to add a Web Service reference and NOT a Service reference. Doing so will alleviate you from having to apply the work-around shown here http://alsaydi.blogspot.com/2009/01/using-report-services-2005-web-service.html 

-- Create your project and start adding the references to the Reporting Services Web Services (ReportExecution and ReportService)
  1. Right click on your project References and select Add Service Reference the following dialg box show come up:


Now hold on. Click on Advanced because what we're trying to add is a Web Service Reference and not a Service Reference. Clicking on advanced gets you the below


Now for compatibility's sake, you have to click on "Add Web Reference ..." button  to get the below dialog box


and in the above dialog, you can now enter the URL for asmx file normally something like

http://ReportingServices_ServerName_Here/ReportServer/ReportService2005.asmx


The service you just added in the above steps will enable your project to call the Web Service to do things such as managing folder, listing reports, setting properties etc. To be able to render reports you need to add the Reporting Service Execution reference which you can do by repeating the above steps but use the URL below instead
 http://ReportingServices_ServerName_Here/ReportServer/ReportExecution2005.asmx

Now your project should be able to use the generated proxy classes to make calls against the reporting services Web Service API. There are some classes that have the same name in ReportingServices namespace and in the ReportingExecution for this reason you'll have to fully qualify your classes when you instantiate; otherwise you get "class name is ambiguous reference between ReportService2005 and ReportExecution2005". Sample code below will illustrate


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ReportingServicesClient.ReportExecution2005;
using ReportingServicesClient.ReportService2005 ;
using System.Web.Services.Protocols;
namespace ReportingServicesClient
{
    
    public class RS2005Client
    {
        private ReportingService2005 rs = null;
        private ReportExecutionService rsExec = null;
        private readonly string htmlformat = "HTML4.0";
        private readonly string excelformat = "EXCEL";
        private readonly string xmlformat = "XML";
        public RS2005Client()
        {
            rs = new ReportingService2005();
            rsExec = new ReportExecutionService();
            rs.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
            rsExec.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
            
        }
        public byte[] RenderReport(string path,ReportFormat frmt)
        {
            string format = null;
            switch (frmt)
            {
                case ReportFormat.HTML:
                    format = htmlformat;
                    break;
                case ReportFormat.Excel:
                    format = excelformat;
                    break;
                case ReportFormat.XML :
                    format = xmlformat;
                    break;
                default:
                    throw new Exception("RenderReport: invalid format supplied");
                    break;
            }
            return RenderReport(path, format);
        }
        private byte[] RenderReport(string path,string format)
        {
            byte[] result = null;
            string reportPath = path;// "/Cosmetic Tracking/Events";
            string historyID = null;
            string devInfo = @"False";
            #region parameters
            // Prepare report parameter.
            /*ParameterValue[] parameters = new ParameterValue[3];
            parameters[0] = new ParameterValue();
            parameters[0].Name = "EmpID";
            parameters[0].Value = "288";
            parameters[1] = new ParameterValue();
            parameters[1].Name = "ReportMonth";
            parameters[1].Value = "6"; // June
            parameters[2] = new ParameterValue();
            parameters[2].Name = "ReportYear";
            parameters[2].Value = "2004";*/
            #endregion                        
            string encoding;
            string mimeType;
            string extension;
            ReportExecution2005.Warning[] warnings = null;
            ReportExecution2005.ParameterValue[] reportHistoryParameters = new ReportExecution2005.ParameterValue[1];
            string[] streamIDs = null;
            ExecutionInfo execInfo = new ExecutionInfo();


            ExecutionHeader execHeader = new ExecutionHeader();
            rsExec.ExecutionHeaderValue = execHeader;
            execInfo = rsExec.LoadReport(reportPath, historyID);

            //rs.SetExecutionParameters(parameters, "en-us"); 
            try
            {
                //rsExec.SetExecutionParameters(reportHistoryParameters,"US-en");                
                result = rsExec.Render(format, devInfo, out extension, out encoding, out mimeType, out warnings, out streamIDs);
                            
            }
            catch (SoapException e)
            {
                throw e;                
            }
            return result;
        }
    }

    public enum ReportFormat
    {
        HTML
        ,Excel
        ,XML
    }
}