Suggested Videos
Part 42 - SessionMode Enumeration in WCF
Part 43 - Single concurrency mode in WCF
Part 44 - Multiple concurrency mode in WCF
In this video we will discuss the Reentrant concurrency mode in WCF with an example.
Reentrant mode allows the WCF service to issue callbacks to the client application.
1. The WCF service concurrency mode is set to Single. This means only one thread is allowed to access the service instance.
2. Client makes a request to the WCF Service. The service instance gets locked by the thread that is executing the client request. At this point no other thread can access the service instance, until the current thread has completed and released the lock.
3. While the service instance is processing the client request, the service wants to callback the client. The callback operation is not one way. This means the response for the callback from the client needs to get back to the same service instance, but the service instance is locked and the response from the client cannot reenter and access the service instance. This situation leads to a deadlock.
There are 2 ways to solve this problem.
1. Set the concurrency mode of the WCF service to Reentrant.
OR
2. Make the callback operation oneway. When the callback operation is made oneway, the service is not expecting a response for the callback from the client, so locking will not be an issue.
Let's understand this with an example
Step 1: Create a class library project with name = Report Service. Delete the autogenerated class1.cs file.
Step 2: Right click on ReportService project in solution explorer and add a WCF service with name = ReportService.
Step 3: Copy and paste the following code in IReportService.cs file
Step 4: Copy and paste the following code in ReportService.cs file
Step 5: Right click on ReportService solution in solution explorer and add a console project with name = Host. We will use this project to host the WCF service.
Step 6: Right click on References folder under Host project and add a reference to ReportService project and System.ServiceModel assembly.
Step 7: Right click on Host project and add Application Configuration file. This should add App.config file.
Step 8: Copy and paste the following configuration in App.config file
Step 9: Copy and paste the following code in Program.cs file in the Host project.
Step 10: Set Host project as the startup project and run the application by pressing CTRL + F5 key. At this point we have the WCF service up and running.
Step 11: Now let's create a client for the WCF service. Create a new windows forms application with name = WindowsClient
Step 12: Add a service reference to ReportService. Right click on References folder and select Add Service Reference. In the Add Service Reference window type Address = http://localhost:8080/ and click GO button. This should bring up the ReportService. In the Namespace textbox type ReportService and click OK.
Step 13: Design Form1 as shown below, with a button and a textbox controls. Set the following properties on button1
Name = btnProcessReport
Text = Process Report
Step 14: Double click on the button to generate the "click event handler" method. Copy and paste the following code in Form1.cs file.
Step 15: Run client the application by pressing CTRL + F5 and click on "Process Report" button. At this point we will get the following exception.
This operation would deadlock because the reply cannot be received until the current Message completes processing. If you want to allow out-of-order message processing, specify ConcurrencyMode of Reentrant or Multiple on ServiceBehaviorAttribute.
Step 16: To fix the above error,
In ReportService.cs file, set ConcurrencyMode to Reentrant as shown below.
OR
Make callback operation contract OneWay in IReportService.cs file in ReportService project
Part 42 - SessionMode Enumeration in WCF
Part 43 - Single concurrency mode in WCF
Part 44 - Multiple concurrency mode in WCF
In this video we will discuss the Reentrant concurrency mode in WCF with an example.
Reentrant mode allows the WCF service to issue callbacks to the client application.
1. The WCF service concurrency mode is set to Single. This means only one thread is allowed to access the service instance.
2. Client makes a request to the WCF Service. The service instance gets locked by the thread that is executing the client request. At this point no other thread can access the service instance, until the current thread has completed and released the lock.
3. While the service instance is processing the client request, the service wants to callback the client. The callback operation is not one way. This means the response for the callback from the client needs to get back to the same service instance, but the service instance is locked and the response from the client cannot reenter and access the service instance. This situation leads to a deadlock.
There are 2 ways to solve this problem.
1. Set the concurrency mode of the WCF service to Reentrant.
OR
2. Make the callback operation oneway. When the callback operation is made oneway, the service is not expecting a response for the callback from the client, so locking will not be an issue.
Let's understand this with an example
Step 1: Create a class library project with name = Report Service. Delete the autogenerated class1.cs file.
Step 2: Right click on ReportService project in solution explorer and add a WCF service with name = ReportService.
Step 3: Copy and paste the following code in IReportService.cs file
using System.ServiceModel;
namespace ReportService
{
// Associate callback contract with service
contract using CallbackContract attribute
[ServiceContract(CallbackContract = typeof(IReportServiceCallback))]
public interface IReportService
{
[OperationContract]
void ProcessReport();
}
// This is the callback contract
public interface IReportServiceCallback
{
// Since we have not set IsOnway=true, the
operation is Request/Reply operation
[OperationContract]
void Progress(int percentageComplete);
}
}
Step 4: Copy and paste the following code in ReportService.cs file
using System.ServiceModel;
using System.Threading;
namespace ReportService
{
public class ReportService : IReportService
{
public void ProcessReport()
{
for (int i = 1; i <= 100; i++)
{
// some logic to
process the report
Thread.Sleep(100);
// Get the callback
channel to send messages to the client
OperationContext.Current.GetCallbackChannel<IReportServiceCallback>().Progress(i);
}
}
}
}
Step 5: Right click on ReportService solution in solution explorer and add a console project with name = Host. We will use this project to host the WCF service.
Step 6: Right click on References folder under Host project and add a reference to ReportService project and System.ServiceModel assembly.
Step 7: Right click on Host project and add Application Configuration file. This should add App.config file.
Step 8: Copy and paste the following configuration in App.config file
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="mexBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="mexBehavior"
name="ReportService.ReportService">
<endpoint address="ReportService" binding="netTcpBinding"
contract="ReportService.IReportService" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080" />
<add baseAddress="net.tcp://localhost:8090" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
Step 9: Copy and paste the following code in Program.cs file in the Host project.
using System.ServiceModel;
using System;
namespace Host
{
class Program
{
public static void Main()
{
using (ServiceHost host = new ServiceHost
(typeof(ReportService.ReportService)))
{
host.Open();
Console.WriteLine("Host started @ " + DateTime.Now.ToString());
Console.ReadLine();
}
}
}
}
Step 10: Set Host project as the startup project and run the application by pressing CTRL + F5 key. At this point we have the WCF service up and running.
Step 11: Now let's create a client for the WCF service. Create a new windows forms application with name = WindowsClient
Step 12: Add a service reference to ReportService. Right click on References folder and select Add Service Reference. In the Add Service Reference window type Address = http://localhost:8080/ and click GO button. This should bring up the ReportService. In the Namespace textbox type ReportService and click OK.
Step 13: Design Form1 as shown below, with a button and a textbox controls. Set the following properties on button1
Name = btnProcessReport
Text = Process Report
Step 14: Double click on the button to generate the "click event handler" method. Copy and paste the following code in Form1.cs file.
using System;
using System.ServiceModel;
using System.Windows.Forms;
namespace WindowsClient
{
// The Form1 class implements
IReportServiceCallback interface
[CallbackBehavior(UseSynchronizationContext =
false)]
public partial class
Form1 : Form, ReportService.IReportServiceCallback
{
public Form1()
{
InitializeComponent();
}
private void btnProcessReport_Click(object sender, EventArgs
e)
{
InstanceContext instanceContext = new InstanceContext(this);
ReportService.ReportServiceClient
client = new
ReportService.ReportServiceClient(instanceContext);
client.ProcessReport();
}
// This method recevies the progress update
from the WCF service
// Report processing percentage is displayed
in the textbox control
public void Progress(int percentageComplete)
{
textBox1.Text = percentageComplete.ToString()
+ " % completed";
}
}
}
Step 15: Run client the application by pressing CTRL + F5 and click on "Process Report" button. At this point we will get the following exception.
This operation would deadlock because the reply cannot be received until the current Message completes processing. If you want to allow out-of-order message processing, specify ConcurrencyMode of Reentrant or Multiple on ServiceBehaviorAttribute.
Step 16: To fix the above error,
In ReportService.cs file, set ConcurrencyMode to Reentrant as shown below.
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class ReportService : IReportService
OR
Make callback operation contract OneWay in IReportService.cs file in ReportService project
public interface IReportServiceCallback
{
[OperationContract(IsOneWay = true)]
void Progress(int percentageComplete);
}
Thank you for great tutorial!
ReplyDeleteI have an exception on a client application when it tries to write into the text box:{"Cross-thread operation not valid: Control 'textBox3' accessed from a thread other than the thread it was created on."}. I tried with: [OperationContract(IsOneWay = true)] and and with [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)], but no luck.
public void ReportProgress(TimeSpan span)
{
//message = span.ToString();
textBox3.Text = "Remaining time is " + span.ToString();
}
What am I missing?
Thank you
Try using this,
ReplyDeleteThis will help you to atleast remove the issue while training yourself.
TextBox.CheckForIllegalCrossThreadCalls = false;