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.
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:
Double click on Queue to view its properties. Select the Body tab and you will see the Patient information as shown below:
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:
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
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Mahesh Sabnis is a DotNetCurry author and a Microsoft MVP having over two decades 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), and Front-end technologies like Angular and React. Follow him on twitter @
maheshdotnet or connect with him on
LinkedIn