-->

18/09/2011

WCF and MSMQ


We know Service and Client interact through EndPoints in WCF.












So clients send their request to the service and the service will receive them Via Endpoints. Thus the request will be processed.
Now, what if Service is down by the time client sends a request.
This is where Message Queuing (MSMQ) comes to picture to provide Message reliability. 

This article will describe how to Create / Test one, by providing step by step description. Before we start anything, go to Programs in control panel, open "Turn on/off windows componenets". There enable all the services pertaining to MSMQ. Also ensure that all the services pertaing to Message Queuing are started in services section.

Step 1: Objective
            I want to write a simple service (File Writer) which accepts user info from client and will write a log to a text file. And implement the message reliability using MSMQ.
Step 2: Create Service Contract.
           I defined a Interface for the file writer service. This interface is called "Contract", which will define the basic skeleton of the service.


          
namespace FileWriterServiceLibrary
{   
    [ServiceContract(Name="http://pratap.com")]    
    public interface IFileWriter
    {   
        [OperationContract(IsOneWay = true)]        
        void WriteUserDataToFile(UserInfo Data);
    }
    [DataContract(Name="http://pratap.com")]
    public class UserInfo
    {
        private string _username;
        private string _state;
        private bool _isadmin;

        [DataMember]
        public string UserName
        {
            get { return _username; }
            set { _username = value; }
        }
        [DataMember]
        public string State
        {
            get { return _state; }
            set { _state = value; }
        }
        [DataMember]
        public bool IsAdmin
        {
            get { return _isadmin; }
            set { _isadmin = value; }
        }
    }
}
Look at that Data contract, that is the class we instantiate to carry the data between service and client.
Step 3: Define the Service implementation.
            All i am trying to do is to get the User info and write it to the file.
namespace FileWriterServiceLibrary
{    
    public class FileWriter : IFileWriter
    {
        public void WriteUserDataToFile(UserInfo Data)
        {
            if (Data != null)
            {              
                File.AppendAllText(@"c:\temp\sampletext.txt",System.Environment.NewLine+"User Name: "+Data.UserName+" ; State: "+ Data.State+" ; Is Admin User: "+Data.IsAdmin.ToString());
                                
            }
        }
    }
}

Step 4: Hosting
            Service is ready, but there should be a hosting environment for this service in order to expose its functionality. Generally it can be done by including simple SVC file in the service project. If you are using WCF 4.0, the frame work itself will take care of hosting , as it offers lot of new features.
            But, here i created a Windows interface to stop and start the Service.
namespace WCFFileWriterService
{
    public partial class Form1 : Form
    {
        ServiceHost SVCHOST; 
        public Form1()
        {
            InitializeComponent();
            lblservicename.Text = "File Writer Service";            
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            try
            {
                string queueName = ".\\private$\\filewriterqueue";
                if (!MessageQueue.Exists(queueName))
                    MessageQueue.Create(queueName, true);   

                Type ServiceType = typeof(FileWriterServiceLibrary.FileWriter);
                SVCHOST = new ServiceHost(ServiceType);
                SVCHOST.Open();
                lblMessage.Text = "Message: File Writer Service is Started";
                tbServiceurl.Text = SVCHOST.BaseAddresses[0].ToString();
            }
            catch (Exception ex) { }
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            try
            {
                if (SVCHOST != null)
                {
                    SVCHOST.Close();
                    SVCHOST = null;
                    lblMessage.Text = "Message: File Writer Service is Stoped";
                }
                else
                {
                    lblMessage.Text = "Message: File Writer Service is already Stoped";
                }                
            }
            catch (Exception ex) { }
        }
        
    }
}
Step 5: Configuration
           Defining the End point and other behavior in config file. I defined only one end point which will be using netMsmqBinding.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="queueName" value=".\\private$\\filewriterqueue"/>
  </appSettings>  
  <system.web>
    <compilation debug="true" />
  </system.web>
  <!-- When deploying the service library project, the content of the config file must be added to the host's 
  app.config file. System.Configuration does not support config files for libraries. -->
  <system.serviceModel>
    <bindings>
      <netMsmqBinding>
        <binding name="DomainlessMsmqBinding" exactlyOnce="true" maxRetryCycles="1000"
          receiveRetryCount="10000" retryCycleDelay="00:00:30">
          <security>
            <transport msmqAuthenticationMode="None" msmqProtectionLevel="None" />
          </security>
        </binding>
      </netMsmqBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="MSMQBindingBehaviour" name="FileWriterServiceLibrary.FileWriter">
        <clear />
        <endpoint address="net.msmq://localhost/private/filewriterqueue"
          binding="netMsmqBinding" bindingConfiguration="DomainlessMsmqBinding"
          name="MSMQENDPOINT" contract="FileWriterServiceLibrary.IFileWriter" />
        <endpoint address="mex" binding="mexHttpBinding" name="mexHttpEndpoint"
        contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8000/FileWriterServiceLibrary/FileWriter" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MSMQBindingBehaviour">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>
Service, Hosting and Endpoint configurations are ready. Please find the below Image for complete structure of the project.

Step 6: Client
            I created a windows application which will be collecting user info, and will send the request to service. All i have to do is refer the service. Click on web reference and the following screen will appear. Once you refer the service with the base address the contract and the method information will be displayed on the screen.
Step 7: Use Proxy
           Once you referred the service, all the config information required to access service will be created in client's configuration file. Proxy object has to be created in client code and use it to send the request.
namespace FileWriterClient
{
    public partial class Form1 : Form
    {
        FileWriterClient.FileWriterSvc.httppratapcom Data;
        FileWriterClient.FileWriterSvc.httppratapcom1Client Proxy;
        public Form1()
        {
            InitializeComponent();
        }

        private void btnSubmit_Click(object sender, EventArgs e)
        {
            
            Data = new FileWriterSvc.httppratapcom(); 
            Data.UserName = tbUserName.Text.ToUpper();
            Data.State = tbState.Text.ToUpper();
            Data.IsAdmin = (bool)cbIsUserAdmin.Checked;
            Proxy = new FileWriterSvc.httppratapcom1Client();
            Proxy.WriteUserDataToFile(Data);          
            
        }
    }
}

The proxy class names will be bit weird, as i have used name space attribute in order to bring uniqueness to the proxy class.
Step 8: Execution
            Now, i will run both Service and Client application. Service was started and submitted a request from client application.
Out Put: The data is written to the text file "c:\temp\sampletext.txt"
Step 9: Test message Reliability
            There is no fun in direct execution. Lets stop the service and try to submit the same user name with 3 different States. "New york", "California", "Las Vegas".
Output: As the service is stopped, none of the last 3 client requests got processed. So, now where are our requests.
Now, go to Computer Management -->Services and Applications -->Message Queuing.
You can see 4 Queues, in which we have our designated Queue in Private Queues. Open it up and you can see all 3 client messages will be waiting for service to be Up.
        So, messages are persistent in MSMQ. Lets see the output by re-starting the service. You can see all the messages in the queue were vanished , as they got processed by service and the out put is:
Finally the question is How much time these messages can wait in Queue. That can be answered by the Binding information you defined in Service configuration.
<netMsmqBinding>
   <binding name="DomainlessMsmqBinding" exactlyOnce="true" maxRetryCycles="1000"
    receiveRetryCount="10000" retryCycleDelay="00:00:30">
      <security>
         <transport msmqAuthenticationMode="None" msmqProtectionLevel="None" />
      </security>
   </binding>
</netMsmqBinding>
Look at that retryCycleDelay which defines, after what time the message in the queue should try to reach service. maxRetryCycles will define, how many such cycles can me made by message before it expires.

Thus the message reliability was implemented in WCF using MSMQ.

Code:
Click Here
Is it helpful for you? Kindly let me know your comments / Questions.

7 comments:

  1. This s very helpful to me... thanks...

    ReplyDelete
  2. Thanks for such an informative post.
    I have one question. Is the sole purpose of wcf is just to write the data to the queue.
    I have a wcf service that is hosted in IIS through WAS.
    I have a queue having same name as that of the service.
    What I want is as soon as some data got entered to the queue the service should be up,read the message from the queue and write it to a log file.
    How to achieve this functionality.
    Any help would be appreciated.

    Thanks in advance,
    Subrat

    ReplyDelete
  3. Can you please upload the project so that we can try on my computer?

    ReplyDelete
  4. Can you please upload the project so that we can try on my computer?

    ReplyDelete
  5. hi sir,
    This was really nice article but giving me an error"System.InvalidOperationException was caught
    Message=Service 'FileWriterServiceLibrary.FileWriter' has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element.
    Source=System.ServiceModel
    StackTrace:
    at System.ServiceModel.Description.DispatcherBuilder.EnsureThereAreApplicationEndpoints(ServiceDescription description)
    at System.ServiceModel.Description.DispatcherBuilder.InitializeServiceHost(ServiceDescription description, ServiceHostBase serviceHost)
    at System.ServiceModel.ServiceHostBase.InitializeRuntime()
    at System.ServiceModel.ServiceHostBase.OnBeginOpen()
    at System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout)
    at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
    at System.ServiceModel.Channels.CommunicationObject.Open()
    at WCFFileWriterService.Form1.btnStart_Click(Object sender, EventArgs e) in C:\testc#\FileWriterServiceLibrary\WCFFileWriterService\Form1.cs:line 47
    InnerException:
    "
    can you please helpn this

    ReplyDelete
    Replies
    1. Make sure both Service and Client hosted in IIS.
      Secondly verify that the example has all the points mention in above description.

      Delete
    2. thanx sir,,,it worked now.Made some changes in config and hosted in IIS.
      great article

      Delete