Ramani Sandeep's Blog

DotNetting – Fast , Easy Way of Developing Applications

Inheritance in WCF

Posted by Ramani Sandeep on May 19, 2013

As a WCF developer you always get question from interviewer : “Can we implement Inheritance in WCF ServiceContract?

Answer will be:

YES we can have Contract Inheritance in WCF. In other words in WCF one ServiceContract can inherit other ServiceContract.

Let us take an example which explain us how to achieve it.

You have a ServiceContract named IParentService as given below :


 [ServiceContract]
 public interface IParentService
 {
    [OperationContract]
    string ParentMessage(string message);
 }

Another ServiceContract named IChildService can inherit IParentService as following :


 [ServiceContract]
 public interface IChildService : IParentService
 {
   [OperationContract]
   string ChildMessage(string message);
 }

Next you need to decide on implemention of Service. Single Service class can implement both contract by implementing bottom most ServiceContract in hierarchy. In this case bottom most ServiceContract is IChildService Service can be implemented as following in a single service class:


 public class Service1 : IChildService
 {
   public string ChildMessage(string message)
   {
     return "Hello " + message + "from Child Service";
   }

   public string ParentMessage(string message)
   {
     return "Hello " + message + "from Parent Service";
   }
 }

Now you have choice that either you can expose whole hierarchy as single EndPoint or different EndPoints for different Service Contract. To expose contract hierarchy create EndPoint with bottom most ServiceContract. In this case we are creating EndPoint with Service Contract IChildService . At the client side operations from whole hierarchy could be invoked.

Hope this will help !!!

Jay Ganesh

About these ads

Posted in WCF | Tagged: , | Leave a Comment »

Method Overloading in WCF

Posted by Ramani Sandeep on May 19, 2013

Method or Operation overloading is one of the most important feature of OOPS. In programming languages like C, C++ and C#, this feature is heavily used by developers. While writing Service definition you may come across when you need to overload operations.

Let us go ahead and write Service with overloaded function as following. In below ServiceContract I am creating two methods and overloading them with different parameters. To achieve overloading at Service side I set Name parameter of both function as follows:


  [OperationContract(Name = "GetIntegerData")]
  string GetData(int value);

  [OperationContract(Name = "GetStringData")]
  string GetData(string value);

So to consume service in my client application, you need to use the method name you specified in the Name attribute. The code in my client application will be:


  ServiceReference1.Service1Client client = new Client.ServiceReference1.Service1Client();
  Console.WriteLine(client.GetIntegerData(5));
  Console.WriteLine(client.GetStringData("pranay"));

Summary : You can easily use method overloading in WCF service with the help of Name field in OperationContract attribute.

Hope this will help !!!

Jay Ganesh

Posted in WCF | Tagged: , | Leave a Comment »

Export to Excel – Multiple GridView into Multiple Worksheet

Posted by Ramani Sandeep on January 18, 2013

There are many article available on web which explain us functionality “Export gridview data into excel“. Which is like “Exporting single gridview data into excel workbook with single worksheet“.

What-if we want to “Export multiple gridview data into multiple worksheets”. So I have decided to write article which explain such scenario.

The focus of the article is Export to Excel functionality – Gridview and its data binding are only for demonstrating the Export functionality.

Here it goes step-by-step code snippets which will help us to achieve the functionality mentioned above.

Step 1: Create simple website and add two gridviews like this:

<form id="form1" runat="server">
<div>
	<h1>Patient Data</h1>
	<br />
	<asp:GridView ID="gvPatient" runat="server">
	</asp:GridView>
	<h1>Student Data</h1>
	<br />
	<asp:GridView ID="gvStudent" runat="server">
	</asp:GridView>
	<br />
	<asp:Button ID="btnExportBoth" runat="server"
		Text="Export Both Grid Data to Excel" onclick="btnExportBoth_Click" />
</div>
</form>

Step 2: Create properties which help us to store the Datatable data.

/// <summary>
/// Property to store patient datatable
/// </summary>
public DataTable PatientData
{
   get { return (DataTable)(ViewState["PatientData"] ?? null); }
   set { ViewState["PatientData"] = value; }
}

/// <summary>
/// Property to store student datatable
/// </summary>
public DataTable StudentData
{
   get { return (DataTable)(ViewState["StudentData"] ?? null); }
   set { ViewState["StudentData"] = value; }
}

Step 3: Page_Load event will load the sample data in Gridview on the page [Patient and Student data respectively]

protected void Page_Load(object sender, System.EventArgs e)
{
	DataTable dt = new DataTable();
	//Patient Data Binding
	dt = GetPatientData();
	gvPatient.DataSource = dt;
	gvPatient.DataBind();
	PatientData = dt;
	//Student Data Binding
	dt = GetStudentData();
	gvStudent.DataSource = dt;
	gvStudent.DataBind();
	StudentData = dt;
}

/// <summary>
/// Get Patient Data - Sample Data creation
/// </summary>
/// <returns></returns>
private DataTable GetPatientData()
{

	// Here we create a DataTable with four columns.
	DataTable table = new DataTable("Patients");
	table.Columns.Add("Dosage", typeof(int));
	table.Columns.Add("Drug", typeof(string));
	table.Columns.Add("Patient", typeof(string));
	table.Columns.Add("Date", typeof(DateTime));

	// Here we add five DataRows.
	table.Rows.Add(25, "Indocin", "David", DateTime.Now);
	table.Rows.Add(50, "Enebrel", "Sam", DateTime.Now);
	table.Rows.Add(10, "Hydralazine", "Christoff", DateTime.Now);
	table.Rows.Add(21, "Combivent", "Janet", DateTime.Now);
	table.Rows.Add(100, "Dilantin", "Melanie", DateTime.Now);
	return table;
}

/// <summary>
/// Get Student Data - Sample Data creation
/// </summary>
/// <returns></returns>
private DataTable GetStudentData()
{

	// Here we create a DataTable with four columns.
	DataTable table = new DataTable("Students");
	table.Columns.Add("SrNo", typeof(int));
	table.Columns.Add("FirstName", typeof(string));
	table.Columns.Add("LastName", typeof(string));
	table.Columns.Add("Age", typeof(int));

	// Here we add five DataRows.
	table.Rows.Add(1, "Sandeep", "Ramani", 29);
	table.Rows.Add(2, "Kapil", "Bhaai", 28);
	table.Rows.Add(3, "Vinit", "Shah", 28);
	table.Rows.Add(4, "Samir", "Bhaai", 30);
	table.Rows.Add(5, "Umang", "Samani", 29);
	return table;
}

Step 4: Here comes the main part which we are waiting for. Export button click event logic along with methods required to create workbook and worksheets.

/// <summary>
/// Export button click event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnExportBoth_Click(object sender, EventArgs e)
{
    object[] myGridViews = new object[2];
    myGridViews[0] = PatientData;
    myGridViews[1] = StudentData;
    CreateWorkBook(myGridViews, "ExportToExcel", 80);
}

/// <summary>
/// Method to create workbook
/// </summary>
/// <param name="cList"></param>
/// <param name="wbName"></param>
/// <param name="CellWidth"></param>
private void CreateWorkBook(object[] cList, string wbName, int CellWidth)
{
   string attachment = "attachment; filename=\"" + wbName + ".xml\"";
   HttpContext.Current.Response.ClearContent();
   HttpContext.Current.Response.AddHeader("content-disposition", attachment);
   HttpContext.Current.Response.ContentType = "application/ms-excel";
   System.IO.StringWriter sw = new System.IO.StringWriter();
   sw.WriteLine("<?xml version=\"1.0\"?>");
   sw.WriteLine("<?mso-application progid=\"Excel.Sheet\"?>");
   sw.WriteLine("<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\"");
   sw.WriteLine("xmlns:o=\"urn:schemas-microsoft-com:office:office\"");
   sw.WriteLine("xmlns:x=\"urn:schemas-microsoft-com:office:excel\"");
   sw.WriteLine("xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\"");
   sw.WriteLine("xmlns:html=\"http://www.w3.org/TR/REC-html40\">");
   sw.WriteLine("<DocumentProperties xmlns=\"urn:schemas-microsoft-com:office:office\">");
   sw.WriteLine("<LastAuthor>Try Not Catch</LastAuthor>");
   sw.WriteLine("<Created>2013-01-09T19:14:19Z</Created>");
   sw.WriteLine("<Version>11.9999</Version>");
   sw.WriteLine("</DocumentProperties>");
   sw.WriteLine("<ExcelWorkbook xmlns=\"urn:schemas-microsoft-com:office:excel\">");
   sw.WriteLine("<WindowHeight>9210</WindowHeight>");
   sw.WriteLine("<WindowWidth>19035</WindowWidth>");
   sw.WriteLine("<WindowTopX>0</WindowTopX>");
   sw.WriteLine("<WindowTopY>90</WindowTopY>");
   sw.WriteLine("<ProtectStructure>False</ProtectStructure>");
   sw.WriteLine("<ProtectWindows>False</ProtectWindows>");
   sw.WriteLine("</ExcelWorkbook>");
   sw.WriteLine("<Styles>");
   sw.WriteLine("<Style ss:ID=\"Default\" ss:Name=\"Normal\">");
   sw.WriteLine("<Alignment ss:Vertical=\"Bottom\"/>");
   sw.WriteLine("<Borders/>");
   sw.WriteLine("<Font/>");
   sw.WriteLine("<Interior/>");
   sw.WriteLine("<NumberFormat/>");
   sw.WriteLine("<Protection/>");
   sw.WriteLine("</Style>");
   sw.WriteLine("<Style ss:ID=\"s22\">");
   sw.WriteLine("<Alignment ss:Horizontal=\"Center\" ss:Vertical=\"Center\" ss:WrapText=\"1\"/>");
   sw.WriteLine("<Borders>");
   sw.WriteLine("<Border ss:Position=\"Bottom\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"");
   sw.WriteLine("ss:Color=\"#000000\"/>");
   sw.WriteLine("<Border ss:Position=\"Left\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"");
   sw.WriteLine("ss:Color=\"#000000\"/>");
   sw.WriteLine("<Border ss:Position=\"Right\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"");
   sw.WriteLine("ss:Color=\"#000000\"/>");
   sw.WriteLine("<Border ss:Position=\"Top\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"");
   sw.WriteLine("ss:Color=\"#000000\"/>");
   sw.WriteLine("</Borders>");
   sw.WriteLine("<Font ss:Bold=\"1\"/>");
   sw.WriteLine("</Style>");
   sw.WriteLine("<Style ss:ID=\"s23\">");
   sw.WriteLine("<Alignment ss:Vertical=\"Bottom\" ss:WrapText=\"1\"/>");
   sw.WriteLine("<Borders>");
   sw.WriteLine("<Border ss:Position=\"Bottom\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"");
   sw.WriteLine("ss:Color=\"#000000\"/>");
   sw.WriteLine("<Border ss:Position=\"Left\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"");
   sw.WriteLine("ss:Color=\"#000000\"/>");
   sw.WriteLine("<Border ss:Position=\"Right\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"");
   sw.WriteLine("ss:Color=\"#000000\"/>");
   sw.WriteLine("<Border ss:Position=\"Top\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"");
   sw.WriteLine("ss:Color=\"#000000\"/>");
   sw.WriteLine("</Borders>");
   sw.WriteLine("</Style>");
   sw.WriteLine("<Style ss:ID=\"s24\">");
   sw.WriteLine("<Alignment ss:Vertical=\"Bottom\" ss:WrapText=\"1\"/>");
   sw.WriteLine("<Borders>");
   sw.WriteLine("<Border ss:Position=\"Bottom\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"");
   sw.WriteLine("ss:Color=\"#000000\"/>");
   sw.WriteLine("<Border ss:Position=\"Left\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"");
   sw.WriteLine("ss:Color=\"#000000\"/>");
   sw.WriteLine("<Border ss:Position=\"Right\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"");
   sw.WriteLine("ss:Color=\"#000000\"/>");
   sw.WriteLine("<Border ss:Position=\"Top\" ss:LineStyle=\"Continuous\" ss:Weight=\"1\"");
   sw.WriteLine("ss:Color=\"#000000\"/>");
   sw.WriteLine("</Borders>");
   sw.WriteLine("<Font ss:Color=\"#FFFFFF\"/>");
   sw.WriteLine("<Interior ss:Color=\"#FF6A6A\" ss:Pattern=\"Solid\"/>");
   //set header colour here
   sw.WriteLine("</Style>");
   sw.WriteLine("</Styles>");
   foreach (DataTable myTable in cList)
   {
      CreateWorkSheet(myTable.TableName, sw, myTable, CellWidth);
   }
   sw.WriteLine("</Workbook>");
   HttpContext.Current.Response.Write(sw.ToString());
   HttpContext.Current.Response.End();
}

/// <summary>
/// Method to create worksheet
/// </summary>
/// <param name="wsName"></param>
/// <param name="sw"></param>
/// <param name="dt"></param>
/// <param name="cellwidth"></param>
private void CreateWorkSheet(string wsName, System.IO.StringWriter sw, DataTable dt, int cellwidth)
{
    if (dt.Columns.Count > 0)
    {
       sw.WriteLine("<Worksheet ss:Name=\"" + wsName + "\">");
       int cCount = dt.Columns.Count;
       long rCount = dt.Rows.Count + 1;
       sw.WriteLine("<Table ss:ExpandedColumnCount=\"" + cCount + "\" ss:ExpandedRowCount=\"" + rCount + "\"x:FullColumns=\"1\"");
       sw.WriteLine("x:FullRows=\"1\">");
       for (int i = (cCount - cCount); i <= (cCount - 1); i++)
       {
          sw.WriteLine("<Column ss:AutoFitWidth=\"1\" ss:Width=\"" + cellwidth + "\"/>");
       }
       DataTableRowIteration(dt, sw);
       sw.WriteLine("</Table>");
       sw.WriteLine("<WorksheetOptions xmlns=\"urn:schemas-microsoft-com:office:excel\">");
       sw.WriteLine("<Selected/>");
       sw.WriteLine("<DoNotDisplayGridlines/>");
       sw.WriteLine("<ProtectObjects>False</ProtectObjects>");
       sw.WriteLine("<ProtectScenarios>False</ProtectScenarios>");
       sw.WriteLine("</WorksheetOptions>");
       sw.WriteLine("</Worksheet>");
   }
}

/// <summary>
/// Method to create rows by iterating thru datatable rows
/// </summary>
/// <param name="dt"></param>
/// <param name="sw"></param>
private void DataTableRowIteration(DataTable dt, System.IO.StringWriter sw)
{
     sw.WriteLine("");
     foreach (DataColumn dc in dt.Columns)
     {
        string tcText = dc.ColumnName;
        sw.WriteLine("<data>" + tcText + "</data>");
     }
     sw.WriteLine("");
     foreach (DataRow dr in dt.Rows)
     {
        sw.WriteLine("");
        foreach (DataColumn tc in dt.Columns)
       {
          string gcText = dr[tc].ToString();
          sw.WriteLine("<data>" + gcText + "</data>");
       }
       sw.WriteLine("");
     }
}

Above code help us to create worksheets as per requirements. You just need to pass the object list [DataTable objects]. So it will iterate thru each one and create worksheets for each of them.

Please make sure you create Datatable with TableName. so that we can create workbook with different worksheet name. I have used Datatable name as worksheet name.

Hope this will help!!!

Jay Ganesh

Posted in ASP.NET, C# 3.0, CodeProject | Tagged: , , , , | Leave a Comment »

How to upgrade SSIS 2005 to 2008 ?

Posted by Ramani Sandeep on September 3, 2012

In our process to upgrade the SSIS 2005 to 2008 packages, we need to be aware of the server being 64 bit for SSIS 2008.

Before running the upgrade package wizard, make following changes in existing packages:

  • All SSIS script tasks – PrecompileScriptintoBinarycode =TRUE [This is our best practice recommendation] as SSIS 2008 does not show this option and is set to true by default.
  • Remove all configuration file references from the package [this should not be there, as in our best practices for package creation, we remove the config file reference].
  • If the Script Task or Script Component references IDTSxxx90 interfaces, then you might need to change these references with the corresponding IDTSxxx100 interfaces before package upgrade.

You cannot change the language from VB (which was only option in SSIS 2005) to C# during the upgrade but you can create a new Script Task or Script Component in after upgrading to SSIS 2008.

Upgrading with the SSIS Package Upgrade Wizard:

SSIS 2008 includes the SSIS Package Upgrade Wizard which can use upgrade packages and also, if required, back up the original packages (though the back-up option works only for the packages stored on the file system). You can launch SSIS Package Upgrade Wizard from command prompt by typing SSISUpgrade.exe (default physical location C:\Program Files\Microsoft SQL Server100DTSBinn folder)

For more information on Upgrade Wizard:

http://www.sql-server-performance.com/2010/upgarde-2005-packages-2008-i/

Changes in SSIS 2008 which we need to consider after the packages are upgraded to 2008.

  • SSIS 2008 uses Visual Studio 2005 Tools for Applications (VSTA) for its scripting environment
  • SSIS 2008 uses a newer build of the SQL Server Native Client
  • SSIS 2008 contains an updated OLEDB Provider for Analysis Services (10.0)

Advantages of VSTA – Visual Studio 2005 Tools for Applications:

All scripts are precompiled in SQL Server 2008 Integration Services (SSIS). When a script is precompiled, the language engine is not loaded at run time and the package runs more quickly. However, precompiled binary files consume significant disk space.

We can divide the changes in SSIS in two categories:

1)      Script task changes and

2)       Connection string changes (this includes the SQL server native client and SSAS connection string changes)

Script task changes:

Error: Name ‘DTS’ is not declared

This problem occurs because the DTS object is not a static global object in SQL Server 2008 script tasks.

To resolve this problem, use one of the following methods:

  • Move the code that depends on the DTS object into the ScriptMain.vb file. The DTS object is an available object in the ScriptMain.vb file.
  • Pass the DTS object as a parameter to methods in the other files in your project.

Connection string changes:

Update connection strings to use the SQL Server 2008 names for the following providers:

  • Analysis Services OLE DB provider
  • SQL Server Native Client

Change the “Provider” portion of the connection string from “SQLNCLI.1” to “SQLNCLI10.1“.  The wizard does not update package configuration files.

Hope this will help !!!

Jay Ganesh

Posted in SQL Server, SSIS | Tagged: , , , , | Leave a Comment »

“Article of the day” @ ASP.NET Microsoft Official Website

Posted by Ramani Sandeep on April 14, 2012

Happy to announce that my article entitled “Add Custom Message Header in WCF 4 Calls” has been selected as “Article of the day” @ASP.NET Microsoft Official Website on April 12, 2012.

You can check this out at : http://www.asp.net/community

Thank you very much to all my readers/well wishers who has inspired me to post/write useful content on the blog. I will try my best to provide useful information in upcoming days as well.

With Regards
Sandeep Ramani

Posted in Mics | Tagged: , | Leave a Comment »

Add Custom Message Header in WCF 4 Calls

Posted by Ramani Sandeep on March 22, 2012

Often, we want to pass some data to some or maybe all our service operations. This data is usually context data such as user tokens, or environmental preferences of the user or machine.

In simple web service we can pass custom header information using Attribute called “[SoapHeaderAttribute ("ServiceHeader", Direction=SoapHeaderDirection.In)]” along with Web Method signatures.

But in WCF, we cannot use the same attribute.

One way would be to pass it as an additional request parameter. But, each and every method call needs to have this parameter(s) repeatedly. Not a very clean solution. Also, if the data type of this parameter changes, all the method signatures and their calls need to be changed.

A nice and easy way to pass that data is to use Message Headers.

In WCF, to pass the custom header information along with method call, we need to implement custom inspector for client and service which will implement the BeforeSendRequest and AfterRecieveRequest methods to inject the custom header.

In order to do this we need following objects/classes:

  1. Soap Header
  2. Message Inspector
  3. Client Context and Server Context class
  4. Custom Behavior

Let’s start creating these classes one by one.

1) Soap Header:

CustomHeader class is used to create custom header for service in which we want to pass header information along with method call. CustomHeader class contains the information that we want to pass along with method call. You can define the structure as per your needs.


    [DataContract]
    public class ServiceHeader
    {

        [DataMember]
        public string EmployeeID { get; set; }

        [DataMember]
        public string WindowsLogonID { get; set; }

        [DataMember]
        public string KerberosID { get; set; }

        [DataMember]
        public string SiteminderToken { get; set; }

    }

    public class CustomHeader : MessageHeader
    {
        private const string CUSTOM_HEADER_NAME = "HeaderName";
        private const string CUSTOM_HEADER_NAMESPACE = "YourNameSpace";

        private ServiceHeader _customData;

        public ServiceHeader CustomData
        {
            get
            {
                return _customData;
            }
        }

        public CustomHeader()
        {
        }

        public CustomHeader(ServiceHeader customData)
        {
            _customData = customData;
        }

        public override string Name
        {
            get { return (CUSTOM_HEADER_NAME); }
        }

        public override string Namespace
        {
            get { return (CUSTOM_HEADER_NAMESPACE); }
        }

        protected override void OnWriteHeaderContents(System.Xml.XmlDictionaryWriter writer, MessageVersion messageVersion)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(ServiceHeader));
            StringWriter textWriter = new StringWriter();
            serializer.Serialize(textWriter, _customData);
            textWriter.Close();

            string text = textWriter.ToString();

            writer.WriteElementString(CUSTOM_HEADER_NAME, "Key", text.Trim());
        }

        public static ServiceHeader ReadHeader(Message request)
        {
            Int32 headerPosition = request.Headers.FindHeader(CUSTOM_HEADER_NAME, CUSTOM_HEADER_NAMESPACE);
            if (headerPosition == -1)
                return null;

            MessageHeaderInfo headerInfo = request.Headers[headerPosition];

            XmlNode[] content = request.Headers.GetHeader<XmlNode[]>(headerPosition);

            string text = content[0].InnerText;

            XmlSerializer deserializer = new XmlSerializer(typeof(ServiceHeader));
            TextReader textReader = new StringReader(text);
            ServiceHeader customData = (ServiceHeader)deserializer.Deserialize(textReader);
            textReader.Close();

            return customData;
        }
    }

As you can see it is a type inheriting from MessageHeader class. Notice the OnWriteHeaderContents override, which is invoked by WCF infrastructure to serialize the SOAP Header, and the ReadHeader static method that we will use later.

2) Message Inspector:

SOAP Header needs to be added by the consumer and read by the service. To do this we need a Message Inspector like the following one:


    /// <summary>
    /// This class is used to inspect the message and headers on the server side,
    /// This class is also used to intercept the message on the client side, before/after any request is made to the server.
    /// </summary>
    public class CustomMessageInspector : IClientMessageInspector, IDispatchMessageInspector
    {
        #region Message Inspector of the Service

        /// <summary>
        /// This method is called on the server when a request is received from the client.
        /// </summary>
        /// <param name="request"></param>
        /// <param name="channel"></param>
        /// <param name="instanceContext"></param>
        /// <returns></returns>
        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            // Create a copy of the original message so that we can mess with it.
            MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
            request = buffer.CreateMessage();
            Message messageCopy = buffer.CreateMessage();

            // Read the custom context data from the headers
            ServiceHeader customData = CustomHeader.ReadHeader(request);

            // Add an extension to the current operation context so that our custom context can be easily accessed anywhere.
            ServerContext customContext = new ServerContext();

            if (customData != null)
            {
                customContext.KerberosID = customData.KerberosID;
                customContext.SiteminderToken = customData.SiteminderToken;
            }
            OperationContext.Current.IncomingMessageProperties.Add("CurrentContext", customContext);
            return null;
        }

        /// <summary>
        /// This method is called after processing a method on the server side and just
        /// before sending the response to the client.
        /// </summary>
        /// <param name="reply"></param>
        /// <param name="correlationState"></param>
        public void BeforeSendReply(ref Message reply, object correlationState)
        {
            // Do some cleanup
            OperationContext.Current.Extensions.Remove(ServerContext.Current);
        }

        #endregion

        #region Message Inspector of the Consumer

        /// <summary>
        /// This method will be called from the client side just before any method is called.
        /// </summary>
        /// <param name="request"></param>
        /// <param name="channel"></param>
        /// <returns></returns>
        public object BeforeSendRequest(ref Message request, IClientChannel channel)
        {
            // Prepare the request message copy to be modified
            MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
            request = buffer.CreateMessage();

            ServiceHeader customData = new ServiceHeader();

            customData.KerberosID = ClientContext.KerberosID;
            customData.SiteminderToken = ClientContext.SiteminderToken;

            CustomHeader header = new CustomHeader(customData);

            // Add the custom header to the request.
            request.Headers.Add(header);

            return null;
        }

        /// <summary>
        /// This method will be called after completion of a request to the server.
        /// </summary>
        /// <param name="reply"></param>
        /// <param name="correlationState"></param>
        public void AfterReceiveReply(ref Message reply, object correlationState)
        {

        }

        #endregion
    }

As you can see from the code sample above, we use the IClientMessageInspector implementation to handle the addition of the header in the consumer-side code, while we use the IDispatchMessageInspector on the service side, to extract the header. It is interesting the FindHeader method of the MessageHeaders collection, as well as the method GetReaderAtHeader, provided by the same collection of Headers. The result of this last method is an XmlDictionaryReader that we use to read our custom header content, through the ReadHeader static method we’ve already introduced.

3) Client Context and Server Context class:

ClientContext class is used to store the header information before calling the method, so when you want to attach the custom header data, you just need to set the values for this ClientContext class. These values get fetched inside BeforeSendRequest method of CustomMessageInspector class and send along with the request made.


    /// <summary>
    /// This class will act as a custom context in the client side to hold the context information.
    /// </summary>
    public class ClientContext
    {
        public static string EmployeeID;
        public static string WindowsLogonID;
        public static string KerberosID;
        public static string SiteminderToken;
    }

At server side, once custom header is received, it will be stored inside this ServerContext class object, so that we can access it anytime once request is received.


    /// <summary>
    /// This class will act as a custom context, an extension to the OperationContext.
    /// This class holds all context information for our application.
    /// </summary>
    public class ServerContext : IExtension<OperationContext>
    {
        public string EmployeeID;
        public string WindowsLogonID;
        public string KerberosID;
        public string SiteminderToken;

        // Get the current one from the extensions that are added to OperationContext.
        public static ServerContext Current
        {
            get
            {
                return OperationContext.Current.Extensions.Find<ServerContext>();
            }
        }

        #region IExtension<OperationContext> Members
        public void Attach(OperationContext owner)
        {
        }

        public void Detach(OperationContext owner)
        {
        }
        #endregion
    }

4) Custom Behavior:

The service will be able to read the Key provided through the custom header simply querying the IncomingMessageProperties dictionary:


OperationContext.Current.IncomingMessageProperties["CurrentContext"];

Of course the Custom Message Inspector needs to be plugged into the WCF pipeline using a custom behavior like the following one.


    /// <summary>
    /// This custom behavior class is used to add both client and server inspectors to
    /// the corresponding WCF end points.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class CustomBehavior : Attribute, IServiceBehavior, IEndpointBehavior
    {
        #region IEndpointBehavior Members

        void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
        }

        void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
        {
            CustomMessageInspector inspector = new CustomMessageInspector();
            clientRuntime.MessageInspectors.Add(inspector);
        }

        void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
        {
            ChannelDispatcher channelDispatcher = endpointDispatcher.ChannelDispatcher;
            if (channelDispatcher != null)
            {
                foreach (EndpointDispatcher ed in channelDispatcher.Endpoints)
                {
                    CustomMessageInspector inspector = new CustomMessageInspector();
                    ed.DispatchRuntime.MessageInspectors.Add(inspector);
                }
            }
        }

        void IEndpointBehavior.Validate(ServiceEndpoint endpoint) { }

        #endregion

        #region IServiceBehavior Members

        void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
        }

        void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription desc, ServiceHostBase host)
        {
            foreach (ChannelDispatcher cDispatcher in host.ChannelDispatchers)
            {
                foreach (EndpointDispatcher eDispatcher in cDispatcher.Endpoints)
                {
                    eDispatcher.DispatchRuntime.MessageInspectors.Add(new CustomMessageInspector());
                }
            }
        }

        void IServiceBehavior.Validate(ServiceDescription desc, ServiceHostBase host) { }

        #endregion
    }

Implement the IEndpointBehavior interface to modify, examine, or extend some aspect of endpoint-wide execution at the application level for either client or service applications.

  • Use the AddBindingParameters method to pass custom data at runtime to enable bindings to support custom behavior.
  • Use the ApplyClientBehavior method to modify, examine, or insert extensions to an endpoint in a client application.
  • Use the ApplyDispatchBehavior method to modify, examine, or insert extensions to endpoint-wide execution in a service application.
  • Use the Validate method to confirm that a ServiceEndpoint meets specific requirements. This can be used to ensure that an endpoint has a certain configuration setting enabled, supports a particular feature and other requirements.

Implement IServiceBehavior to modify, examine, or extend some aspect of service-wide execution at the application level:

  • Use the ApplyDispatchBehavior method to change run-time property values or insert custom extension objects such as error handlers, message or parameter interceptors, security extensions, and other custom extension objects.
  • Use the Validate method to examine the description before constructs the executing service to confirm that it can execute properly.
  • Use the AddBindingParameters method to pass to a binding element the custom information for the service so that it can support the service correctly.

Adding Behavior to the Runtime

When you construct a ServiceHost or client-side ChannelFactory, the runtime reflects over the service types, reads the configuration file, and starts building an in-memory description of the service. Within ServiceHost, this description is made available to you via the Description property (of type ServiceDescription). Within ChannelFactory, it’s made available via the Endpoint property (of type ServiceEndpoint); the client-side description is limited to the target endpoint.

The ServiceDescription contains a full description of the service and each endpoint (ServiceEndpoint), including contracts (ContractDescription) and operations (OperationDescription). ServiceDescription provides a Behaviors property (a collection of type IServiceBehavior) that models a collection of service behaviors. Each ServiceEndpoint also has a Behaviors property (a collection of type IEndpointBehavior) that models the individual endpoint behaviors. Likewise, ContractDescription and OperationDescription each have an appropriate Behaviors property.

These behavior collections are automatically populated during the ServiceHost and ChannelFactory construction process with any behaviors that are found in your code (via attributes) or within the configuration file (more on this shortly). You can also add behaviors to these collections manually after construction. The following example shows how to add the CustomBehavior to the host as a service behavior:


WCFServiceClient ws = new WCFServiceClient();
ws.ChannelFactory.Endpoint.Behaviors.Add(new CustomBehavior());

Adding Behavior with Attribute

During the ServiceHost/ChannelFactory construction process, the runtime reflects over the service types and configuration file and automatically adds any behaviors it finds to the appropriate behavior collections in the ServiceDescription.


  /// <summary>
    /// Summary description for WCFService
    /// </summary>
    [CustomBehavior]
    public class WCFService : IWCFService
    {
    }

That’s all! Enjoy your custom header passing using behavior specified.

Hope this will help!!!

Jay Ganesh

Posted in CodeProject, WCF | Tagged: , , , , , | 1 Comment »

“Article of the day” @ ASP.NET Forum

Posted by Ramani Sandeep on December 16, 2011

I am happy to announce that my article entitled “Parallelism in .NET – PLINQ” has been selected as “Article of the day” @ ASP.NET Forum on Dec 15, 2011.

You can check this out at : http://www.asp.net/community

Thanks to all my readers/well wishers who has inspired me to post/write useful content on the blog. I will try my best to provide useful information in upcoming days as well.

With Regards
Sandeep Ramani

Posted in Mics | 7 Comments »

“Article of the day” @ ASP.NET Forum

Posted by Ramani Sandeep on December 2, 2011

I am happy to announce that my article entitled “No more pain to configure WCF 4 Services” has been selected as “Article of the day” @ ASP.NET Forumn on Dec 1, 2011.

You can check this out at : http://www.asp.net/community

Thanks to all my readers/well wishers who has inspired me to post/write useful content on the blog. I will try my best to provide useful information in upcoming days as well.

With Regards
Sandeep Ramani

Posted in Mics | Tagged: | 1 Comment »

No more pain to configure WCF 4 services

Posted by Ramani Sandeep on November 18, 2011

Developer who has worked with ASP.NET Web Services (ASMX) and WCF always feels that using predecessor was much more easier. It just because WCF configuration is much more complex as compare to ASP.NET Web Service.

WHY ??????

With ASMX, you were able to define a [WebMethod] operation and the runtime automatically provided a default configuration for the underlying communications. When moving to WCF 3.x, on the other hand, developers have to know enough about the various WCF configuration options to define at least one endpoint.

In an effort to make the overall WCF experience just as easy as ASMX, WCF 4 comes with a new “default configuration” model that completely removes the need for any WCF configuration. If you don’t provide any WCF configuration for a particular service, the WCF 4 runtime automatically configures your service with some standard endpoints and default binding/behavior configurations. This makes it much easier to get a WCF service up and running, especially for those who aren’t familiar with the various WCF configuration options.

Let’s discuss some of the standard configuration options that WCF 4 support :

1) Default Endpoints
2) Default Protocol Mapping
3) Default Binding Configurations
4) Default Behavior Configurations

1) Default Endpoints

With WCF 3.X, if you try to host a service without configured endpoints, ServiceHost instance will throw an exception informing you that you need to configure at least one endpoint. With WCF 4, this is no longer the case because the runtime automatically adds one or more ‘default endpoints’ for you.

Now question comes in mind, How this is done by WCF 4?

Answer to Question is like : When Host application calls Open method on ServiceHost instance, it build internal service description from the application configuration file. Than it check the count of configured endpoints. if it is still zero than it will call “AddDefaultEndpoints” public method and method will adds one default endpoint per base address for each service contract implemented by the service.

Clear or Confused ??

Lets take one example to be more clear on it.

If the service implements two service contracts and you configure the host with a single base address, AddDefaultEndpoints will configure the service with two default endpoints (one for each service contract). However, if the service implements two service contracts and the host is configured with two base addresses (one for HTTP and one for TCP), AddDefaultEndpoints will configure the service with four default endpoints.

I Hope now its clear….if still not…please go thru the link provided for more details on it.

-> New Features in WCF 4 that Will Instantly Make You More Productive
(http://www.code-magazine.com/Article.aspx?quickid=1006061)

2) Default Protocol Mapping

In .Net 4.0 framework, default protocol mapping between transport protocol schemes and the built in WCF bindings are as follows :


   <protocolMapping>
      <add scheme="http" binding="basicHttpBinding" bindingConfiguration="" />
      <add scheme="net.tcp" binding="netTcpBinding" bindingConfiguration=""/>
      <add scheme="net.pipe" binding="netNamedPipeBinding" bindingConfiguration=""/>
      <add scheme="net.msmq" binding="netMsmqBinding" bindingConfiguration=""/>
   </protocolMapping>

You can override these mappings at machine level by adding this section to machine.config file and modify the bindings as per your needs.

If you want to override this mappings at application level than you can override the above section in application/web configfile.

3) Default Binding Configurations

In WCF 3.x , binding can be done like this :


<configuration>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicBindingMtom" messageEncoding="Mtom"/>
      </basicHttpBinding>
    </bindings>
    <services>
      <service name="HelloService">
        <endpoint address="mtom" binding="basicHttpBinding"
                  bindingConfiguration="BasicBindingMtom"
                  contract="IHello"/>
      </service>
    </services>
  </system.serviceModel>
</configuration>

Here “BasicBindingMtom” binding configuration overrides the defaults for BasicHttpBinding by changing the message encoding to “Mtom”. However this binding will effect only when you apply it to a specific endpoint thru “bindingConfiguration” attribute.

With WCF 4, binding can be done like this :


      <basicHttpBinding>
        <binding messageEncoding="Mtom"/>
      </basicHttpBinding>

No name attribute required. This feature gives you a simple mechanism to define a standard set of binding defaults that you can use across all your services without imposing any complexities of binding configurations.

4) Default Behavior Configurations

With WCF 4, it is possible to define default behavior configurations for services and endpoints.


    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>

Above configuration turns on the metadata for any service that doesn’t come with explicit behavior configuration.

In WCF 4, Behavior configuration also support inheritance model. It means that if application defines a behavior using same name as one already defined in machine.config, the application specific behavior configuration will get merged with machine configuration.

With these new additions in the WCF 4, it will be easier for developers to configure the services. I am sure that many developers will feel relaxed by having these new features in WCF and also start using it. Thats all from my side for this post.

Hope this will help !!!

Jay Ganesh ……

Posted in CodeProject, WCF, Web Services | Tagged: , , , , , , , , | 2 Comments »

DataContract Vs MessageContract

Posted by Ramani Sandeep on October 19, 2011

1. Comparison:

Data Contracts:

WCF data contracts provide a mapping function between .NET CLR types that are defined in code and XML Schemas Definitions defined by the W3C organization (www.w3c.org/) that are used for communication outside the service.

you can say “Data contract is a formal agreement between a service and a client that abstractly describes the data to be exchanged”. That is, to communicate, the client and the service do not have to share the same types, only the same data contracts. A data contract precisely defines, for each parameter or return type, what data is serialized (turned into XML) to be exchanged.

Message Contracts:

Message contracts describe the structure of SOAP messages sent to and from a service and enable you to inspect and control most of the details in the SOAP header and body. Whereas data contracts enable interoperability through the XML Schema Definition (XSD) standard, message contracts enable you to interoperate with any system that communicates through SOAP.

Using message contracts gives you complete control over the SOAP message sent to and from a service by providing access to the SOAP headers and bodies directly. This allows use of simple or complex types to define the exact content of the SOAP parts.

2. Why use MessageContract when DataContract is there?

Data contracts are used to define the data structure. Messages that are simply a .NET type, lets say in form of POCO (plain old CLR object), and generate the XML for the data you want to pass.

Message contracts are preferred only when there is a need to “control” the layout of your message(the SOAP message); for instance, adding specific headers/footer/etc to a message.

sometimes complete control over the structure of a SOAP message is just as important as control over its contents. This is especially true when interoperability is important or to specifically control security issues at the level of the message or message part. In these cases, you can create a message contract that enables you to use a type for a parameter or return value that serializes directly into the precise SOAP message that you need.

3. Why we use MessageContract to pass SOAP headers ?

Passing information in SOAP headers is useful if you want to communicate information “out of band” from the operation signature.

For instance, session or correlation information can be passed in headers, rather than adding additional parameters to operations or adding this information as fields in the data itself.

Another example is security, where you may want to implement a custom security protocol (bypassing WS-Security) and pass credentials or tokens in custom SOAP headers.

A third example, again with security, is signing and encrypting SOAP headers, where you may want to sign and/or encrypt some or all header information. All these cases can be handled with message contracts. The downside with this technique is that the client and service must manually add and retrieve the information from the SOAP header, rather than having the serialization classes associated with data and operation contracts do it for you.

4. Can’t mix datacontracts and messagecontracts.

Because message-based programming and parameter-based programming cannot be mixed, so you cannot specify a DataContract as an input argument to an operation and have it return a MessageContract, or specify a MessageContract as the input argument to an operation and have it return a DataContract. You can mix typed and untyped messages, but not messageContracts and DataContracts. Mixing message and data contracts will cause a runtime error when you generate WSDL from the service.

Hope this will help !!!

@@@ Happy Diwali @@@

Posted in CodeProject, WCF, Web Services | Tagged: , , , , | Leave a Comment »

 
Follow

Get every new post delivered to your Inbox.

Join 124 other followers