Integrating Traditional .NET MSMQ Application with Windows Communication Foundation 3.5 using MsmqIntegrationBinding

Posted by: Mahesh Sabnis , on 11/20/2009, in Category Windows Communication Foundation (WCF)
Views: 38719
Abstract: Most of you might have already started working with WCF for developing SOA based enterprise applications. WCF has provided several benefits for distributed application development e.g. Security, Sessions, Callback etc. One of the nice features with WCF is integration with Microsoft Message Queuing (MSMQ). In this article, we will see how to integrate traditional .NET MSMQ Application with Windows Communication Foundation 3.5 using MsmqIntegrationBinding
Most of you might have already started working with WCF for developing SOA based enterprise applications. WCF has provided several benefits for distributed application development e.g. Security, Sessions, Callback etc. One of the nice features with WCF is integration with Microsoft Message Queuing (MSMQ). For developing reliable applications in disconnected environment, MSMQ plays a very important role. The sender application sends message to the receiver which is responsible for processing messages, but if the receiver is not available, then this message must be stored in MSMQ. When the receiver is ready, it receives messages and processes them. This technique provides message delivery guarantee.
The question that arises here is how to make it possible for WCF applications to use MSMQ benefits for reliable communication? For this purpose .NET 3.x, has provided the following two binding techniques:
1.    NetMsmqBinding.
2.    MsmqIntegratedBinding.
NetMsmqBinding, uses MSMQ for transport of messages. In this case, both the WCF service and Client has to use ‘netMsmqBinding’. This automatically takes care of accepting messages from client, storing it in MSMQ and when the service is ready, automatically receiving and processing these messages. NetMsmqBinding works with MSMQs in active directory, specific to machine etc. Following is the address mechanism used in case of NetMsmqBinding:
net.msmq://<machine-name>/<private or public store>/<msmq-name>
Eg:
net.msmq://serverpc/DataQ (here I am assuming that Q is public)
MsmqIntegratedBinding, on the other hand works with traditional MSMQ based applications. Here the client application need not have knowledge of the WCF service, it should only know the MSMQ address. This technique provides better integration between all applications e.g. old legacy clients to send messages using MSMQ to WCF services. In case of MsmqIntegrationBinding, the address mechanism used is:
msmq.formatname:DIRECT=OS:.\private$\PatientQ
In the following example, I will be using a private queue. I am using a Windows Presentation Foundation (WPF) client application. This application is developed taking into context the Patient Information Management system, where the operator registers a patient and this information is then sent to database using our WCF service.
Task 1: Creating WCF service
In this task, we will create a WCF service which exposes operation contract and accepts patient information if it is present in queue. The WCF service is self hosted for demo purpose. You can also host it in windows service.
Step 1: Open VS 2008 and create a blank solution, name this as ‘WCF_UsingNetMsmqIntegrationBinding’. In this project, add a class library project, name this as ‘BusinessDataObject’. This project contains a class which will hold values of Patient entered by client application. The code is as below:
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace BusinessDataObject
{
    [Serializable]
    public class PatientMaster
    {
        public int PatientId { get; set; }
        public string PatientName { get; set; }
        public string PatientAddress { get; set; }
        public int PatientAge { get; set; }
  
}
VB.NET
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
 
Namespace BusinessDataObject
      <Serializable> _
      Public Class PatientMaster
            Private privatePatientId As Integer
            Public Property PatientId() As Integer
                  Get
                        Return privatePatientId
                  End Get
                  Set(ByVal value As Integer)
                        privatePatientId = value
                  End Set
            End Property
            Private privatePatientName As String
            Public Property PatientName() As String
                  Get
                        Return privatePatientName
                  End Get
                  Set(ByVal value As String)
                        privatePatientName = value
                  End Set
            End Property
            Private privatePatientAddress As String
            Public Property PatientAddress() As String
                  Get
                        Return privatePatientAddress
                  End Get
                  Set(ByVal value As String)
                        privatePatientAddress = value
                  End Set
            End Property
            Private privatePatientAge As Integer
            Public Property PatientAge() As Integer
                  Get
                        Return privatePatientAge
                  End Get
                  Set(ByVal value As Integer)
                        privatePatientAge = value
                  End Set
            End Property
 
End Class
Note that the class is marked as [Serializable] because patient data will be passed as a message body to MSMQ. Build this project.
Step 2: In the solution, right click and add a new WPF application, name this as ‘WPF_WCFServiceHost’. This project will create a MSMQ, if it not present.
Note: I am doing a Queue creation here just for demo purpose, you can use a Queue which is either already available or created by some other application.
To this project, add following references:
·         BusinessDataObject.dll
·         System.ServiceModel, used for WCF service hosting
·         System.Messaging, used for MSMQ creation
Open Window1.xaml and write the following Xaml code:
<Window x:Class="WPF_WCFServiceHost.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="799"
         Loaded="Window_Loaded" Closed="Window_Closed">
    <Grid>
        <TextBlock Height="41" Margin="36,25,120,0" Name="txtRecMessages"
                   VerticalAlignment="Top"  FontSize="30" FontWeight="Bold"
                    Text="Reciving Messages From:" Foreground="Red"/>
    </Grid>
</Window>
 
 
Step 3: To this project, add a new interface ‘IService’, which will expose an operation contract as shown below:
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using System.ServiceModel;
using System.ServiceModel.MsmqIntegration;
 
using BusinessDataObject;
namespace WPF_WCFServiceHost
{
    [ServiceContract]
    [ServiceKnownType(typeof(PatientMaster))]  
    public interface IService
    {
        [OperationContract(IsOneWay=true,Action="*")]
        void RegisterPatient(MsmqMessage<PatientMaster> objPatient);
    }
}
VB.NET
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
 
Imports System.ServiceModel
Imports System.ServiceModel.MsmqIntegration
 
Imports BusinessDataObject
Namespace WPF_WCFServiceHost
      <ServiceContract, ServiceKnownType(GetType(PatientMaster))> _
      Public Interface IService
            <OperationContract(IsOneWay:=True,Action:="*")> _
            Sub RegisterPatient(ByVal objPatient As MsmqMessage(Of PatientMaster))
      End Interface
End Namespace
In the above code, the important point to note is that the method ‘RegisterPatient’ accepts ‘MsmqMessage<T>’ class object as a parameter. This class provides encapsulations to messages send and received over MSMQ integrated channel, to and from existing MSMQ application. Another important part is that the ‘OperationContract’ attribute is provided with values ‘IsOneWay=true’ which indicates that the message will not generate any response immediately. The other parameter is ‘Action=”*”’ which defines WS-addressing action of the request message, meaning that when a legacy client or .NET client sends messages to MSMQ, this method will automatically accept request and start executing.
Step 4: In the WPF project add a new class, name this as ‘CService’. Implement ‘IService’ in this class as shown below:
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using BusinessDataObject;
using System.ServiceModel.MsmqIntegration;
using System.ServiceModel;
 
namespace WPF_WCFServiceHost
{
    public class CService : IService
    {
 
        #region IService Members
        [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
        public void RegisterPatient(MsmqMessage<PatientMaster> objPatient)
        {
            SqlConnection Conn = new SqlConnection("Data Source=.;Initial Catalog=Medical;Integrated Security=SSPI");
            SqlCommand Cmd = new SqlCommand();
            string InsSql = null;
            Conn.Open();
            Cmd.Connection = Conn;
 
            PatientMaster objPat = objPatient.Body;
 
            InsSql = "Insert into PatientMaster Values(@PatientName,@PatientAddress,@PatientAge)";
 
            Cmd.CommandText = InsSql;
            Cmd.Parameters.AddWithValue("@PatientName", objPat.PatientName);
            Cmd.Parameters.AddWithValue("@PatientAddress", objPat.PatientAddress);
            Cmd.Parameters.AddWithValue("@PatientAge", objPat.PatientAge);
 
            Cmd.ExecuteNonQuery();
 
            Conn.Close();
            Conn.Dispose();
        }
 
        #endregion
    }
}
 
VB.NET
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Data
Imports System.Data.SqlClient
Imports BusinessDataObject
Imports System.ServiceModel.MsmqIntegration
Imports System.ServiceModel
 
Namespace WPF_WCFServiceHost
      Public Class CService
            Implements IService
 
            #Region "IService Members"
            <OperationBehavior(TransactionScopeRequired := True, TransactionAutoComplete := True)> _
            Public Sub RegisterPatient(ByVal objPatient As MsmqMessage(Of PatientMaster))
                  Dim Conn As New SqlConnection("Data Source=.;Initial Catalog=Medical;Integrated Security=SSPI")
                  Dim Cmd As New SqlCommand()
                  Dim InsSql As String = Nothing
                  Conn.Open()
                  Cmd.Connection = Conn
 
                  Dim objPat As PatientMaster = objPatient.Body
 
                  InsSql = "Insert into PatientMaster Values(@PatientName,@PatientAddress,@PatientAge)"
 
                  Cmd.CommandText = InsSql
                  Cmd.Parameters.AddWithValue("@PatientName", objPat.PatientName)
                  Cmd.Parameters.AddWithValue("@PatientAddress", objPat.PatientAddress)
                  Cmd.Parameters.AddWithValue("@PatientAge", objPat.PatientAge)
 
                  Cmd.ExecuteNonQuery()
 
                  Conn.Close()
                  Conn.Dispose()
            End Sub
 
            #End Region
      End Class
End Namespace
 
Step 5: The most important part is in this step. To the project add ‘App.Config’ file and write the following configuration:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <appSettings>
    <add key="qName" value=".\private$\PatientQ"/>
 </appSettings>
 <system.serviceModel>
    <services>
      <service name="WPF_WCFServiceHost.CService">
        <endpoint
          address="msmq.formatname:DIRECT=OS:.\private$\PatientQ"
           binding="msmqIntegrationBinding"
           bindingConfiguration="IntQBind"
           contract="WPF_WCFServiceHost.IService"/>
      </service>
    </services>
    <bindings>
      <msmqIntegrationBinding>
        <binding name="IntQBind">
          <security mode ="None"></security>
        </binding>
      </msmqIntegrationBinding>
    </bindings>
 </system.serviceModel>
</configuration>
 
The endpoint here holds address of the private queue created.
Step 6: Open Window.xaml.cs and write the following code:
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Messaging;
using System.Configuration;
using System.ServiceModel;
 
namespace WPF_WCFServiceHost
{
    ///<summary>
    /// Interaction logic for Window1.xaml
    ///</summary>
    public partial class Window1 : Window
    {
        ServiceHost Host;
        public Window1()
        {
            InitializeComponent();
        }
 
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            string qName = ConfigurationSettings.AppSettings["qName"].ToString();
 
            MessageQueue mq = null;
            if (!MessageQueue.Exists(qName))
            {
                mq = MessageQueue.Create(qName, true);
            }
 
            Host = new ServiceHost(typeof(CService));
            Host.Open();
 
            txtRecMessages.Text += qName;
 
        }
 
        private void Window_Closed(object sender, EventArgs e)
        {
            Host.Close();
        }
    }
}
 
VB.NET
 
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Messaging
Imports System.Configuration
Imports System.ServiceModel
 
Namespace WPF_WCFServiceHost
      ''' <summary>
      ''' Interaction logic for Window1.xaml
      ''' </summary>
      Partial Public Class Window1
            Inherits Window
            Private Host As ServiceHost
            Public Sub New()
                  InitializeComponent()
            End Sub
 
            Private Sub Window_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
                  Dim qName As String = ConfigurationSettings.AppSettings("qName").ToString()
 
                  Dim mq As MessageQueue = Nothing
                  If (Not MessageQueue.Exists(qName)) Then
                        mq = MessageQueue.Create(qName, True)
                  End If
 
                  Host = New ServiceHost(GetType(CService))
                  Host.Open()
 
                  txtRecMessages.Text += qName
 
            End Sub
 
            Private Sub Window_Closed(ByVal sender As Object, ByVal e As EventArgs)
                  Host.Close()
            End Sub
      End Class
End Namespace
 
Step 7: Build and run the application, you will find queue of name ‘PatientQ’ created.
Task 2: Creating client application.
In this task we will create a WPF client application and send the patient information to the Queue created.
Step 1: In the solution created in Task 1, add a new WPF application and name this as ‘WPF_ClientDataSernder’.
Step 2: To this project, add following references:
·         BusinessDataObject.dll.
·         System.Messaging, used for MSMQ programming.
·         System.Transaction.
Step 3: Add the following xaml code in Window1.xaml file:
<Window x:Class="WPF_ClientDataSernder.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="691"
        Loaded="Window_Loaded">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="46*" />
            <RowDefinition Height="61*" />
            <RowDefinition Height="58*" />
            <RowDefinition Height="97*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="312*" />
            <ColumnDefinition Width="357*" />
        </Grid.ColumnDefinitions>
        <TextBlock Name="textBlock1"  Text="Patient Name" Foreground="Red" FontSize="20" FontWeight="Bold"/>
        <TextBlock Grid.Row="1" Name="textBlock2" Text="Patient Address" Foreground="Red" FontSize="20" FontWeight="Bold"/>
        <TextBlock Grid.Row="2" Name="textBlock3" Text="Patient Age" Foreground="Red" FontSize="20" FontWeight="Bold"/>
        <TextBox Grid.Column="1" Name="txtpname"
                  Text="{Binding PatientName}"/>
        <TextBox Grid.Column="1" Grid.Row="1" Name="txtpaddress"
                 Text="{Binding PatientAddress}"/>
        <TextBox Grid.Row="2" Name="txtpage" Grid.Column="1"
                 Text="{Binding PatientAge}"/>
        <Button Grid.Column="1"
                Grid.Row="3" Margin="0,31,0,32"
                Name="btnSave"
                 Click="btnSave_Click">Save</Button>
    </Grid>
</Window>
 
TextBoxes are bound with public properties of ‘PatientMaster’ class.
Step 4: In the project add a new App.Config file and add the following configuration:
<?xmlversion="1.0"encoding="utf-8" ?>
<configuration>
    <appSettings>
        <addkey="qName"value=".\private$\PatientQ"/>
    </appSettings>
</configuration>
 
Step 5: In the Window1.xaml.cs write the following code:
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using BusinessDataObject;
using System.Messaging;
using System.Configuration;
using System.Transactions;
 
namespace WPF_ClientDataSernder
{
    ///<summary>
    /// Interaction logic for Window1.xaml
    ///</summary>
    public partial class Window1 : Window
    {
        PatientMaster objPatient;
        public Window1()
        {
            InitializeComponent();
        }
 
        private void btnSave_Click(object sender, RoutedEventArgs e)
        {
            MessageQueue patientQueue;
            try
            {
                string qName = ConfigurationSettings.AppSettings["qName"].ToString();
                patientQueue = new MessageQueue(@"FormatName:Direct=OS:" + qName);
 
                //Store Patient Information
 
                Message patientMessage = new Message();
 
                patientMessage.Body = objPatient;
                patientMessage.Label = "PatientInfo";
 
                //Now Create Transaction Scope
 
                using (TransactionScope tran = new TransactionScope(TransactionScopeOption.Required))
                {
                    patientQueue.Send(patientMessage, MessageQueueTransactionType.Automatic);
                    tran.Complete();
                }
 
           }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
 
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            objPatient = new PatientMaster();
            this.DataContext = objPatient;
        }
    }
}
 
VB.NET
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Windows
Imports BusinessDataObject
Imports System.Messaging
Imports System.Configuration
Imports System.Transactions
 
Namespace WPF_ClientDataSernder
      ''' <summary>
      ''' Interaction logic for Window1.xaml
      ''' </summary>
      Partial Public Class Window1
            Inherits Window
            Private objPatient As PatientMaster
            Public Sub New()
                  InitializeComponent()
            End Sub
 
            Private Sub btnSave_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
                  Dim patientQueue As MessageQueue
                  Try
                        Dim qName As String = ConfigurationSettings.AppSettings("qName").ToString()
                        patientQueue = New MessageQueue("FormatName:Direct=OS:" & qName)
 
                        'Store Patient Information
 
                        Dim patientMessage As New Message()
 
                        patientMessage.Body = objPatient
                        patientMessage.Label = "PatientInfo"
 
                        'Now Create Transaction Scope
 
                        Using tran As New TransactionScope(TransactionScopeOption.Required)
                              patientQueue.Send(patientMessage, MessageQueueTransactionType.Automatic)
                              tran.Complete()
                        End Using
 
                  Catch ex As Exception
                        MessageBox.Show(ex.Message)
                  End Try
            End Sub
 
            Private Sub Window_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
                  objPatient = New PatientMaster()
                  Me.DataContext = objPatient
            End Sub
      End Class
End Namespace
 
The above code on the button click creates an object of the ‘Message’ class which contains patient information encapsulated inside it. The MSMQ object is created based upon the Queue name in App.Config file. Once the patient information is added to the ‘objPatient’ object, the ‘Message’ object contains the patent information in its body and is then passed to the queue. While sending message, MSMQ uses component services (MTS or COM+) context to send or receive messages using ‘MessageQueueTransactionType.Automatic’.
Step 6: Run the client application WPF application, enter patient information and click the ‘Save’ button.
Window2
Step 7: Right click on My Computer, or Computer if you are using Windows 7 or Windows Vista, and select ‘Manage’. You will see a Queue has been created and a message is displayed as shown below:
ComputerManagement
Double click on Queue to view its properties. Select the Body tab and you will see the Patient information as shown below:
PatientInfoProperties
Stop the client application.
Step 8: Run the ‘WPF_WCFServiceHost’ project and wait for some time. Then open your SqlServer and you will see that the patient record is added into the PatientMaster table.
The Sql Server output will be similar to the one shown below:
SQLServer
Now, if you again visit the MSMQ window, you will still have message there, but you cannot open it because it is already processed. Just refresh window and this message will disappear.
Further more, you can implement MSMQ triggering mechanism for running notification application.
Conclusion: MsmqIntergrationBinding provides facility to integrate WCF application with existing .NET applications or any other legacy application which is using MSMQ, for reliable messaging. The entire source code of this article can be downloaded over here
Give a +1 to this article if you think it was well written. Thanks!
Recommended Articles
Mahesh is having 10 years of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions). Follow him on twitter @maheshdotnet


Page copy protected against web site content infringement by Copyscape


User Feedback
Comment posted by Anup Hosur on Saturday, November 21, 2009 6:58 AM
nice article. your article is much better than mine, i wrote it in dotnetfunda website.  
Comment posted by Jim Hewgill on Sunday, November 22, 2009 2:27 AM
Mahesh thanks for another good article. Your articles on .NET 4.0 have been extremely helpful from an overview perspective. It is also very kind of you to write the articles in both C# and VB.
Comment posted by Huy Smotricz on Wednesday, November 25, 2009 1:13 PM
Thanks for this writeup. The step by step approach shown here is easier to follow
Comment posted by Manish Khobragade on Friday, June 17, 2011 12:17 AM
Thanks. Nice article.

Post your comment
Name:  
E-mail: (Will not be displayed)
Comment:
Insert Cancel