4

1.) I have a main method Processing, which takes string as an arguments and that string contains some x number of tasks.

2.) I have another method Status, which keeps track of first method by using two variables TotalTests and CurrentTest. which will be modified every time with in a loop in first method(Processing).

3.) When more than one client makes a call parallely to my web service to call the Processing method by passing a string, which has different tasks will take more time to process. so in the mean while clients will be using a second thread to call the Status method in the webservice to get the status of the first method.

4.) when point number 3 is being done all the clients are supposed to get the variables(TotalTests,CurrentTest) parallely with out being mixed up with other client requests.

5.) The code that i have provided below is getting mixed up variables results for all the clients when i make them as static. If i remove static for the variables then clients are just getting all 0's for these 2 variables and i am unable to fix it. Please take a look at the below code.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class Service1 : IService1
{
    public int TotalTests = 0;
    public int CurrentTest = 0;

    public string Processing(string OriginalXmlString)
    {
                XmlDocument XmlDoc = new XmlDocument();
                XmlDoc.LoadXml(OriginalXmlString);
                this.TotalTests = XmlDoc.GetElementsByTagName("TestScenario").Count;  //finding the count of total test scenarios in the given xml string
                this.CurrentTest = 0;
                while(i<10)
                {
                        ++this.CurrentTest;
                         i++;
                }
    }

    public string Status()
    {
        return (this.TotalTests + ";" + this.CurrentTest);
    }
}

server configuration

<wsHttpBinding>
    <binding name="WSHttpBinding_IService1" closeTimeout="00:10:00"
      openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00"
      bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
      maxBufferPoolSize="524288" maxReceivedMessageSize="2147483647"
      messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
      allowCookies="false">
      <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
        maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
      <reliableSession ordered="true" inactivityTimeout="00:10:00"
        enabled="true" />
      <security mode="Message">
        <transport clientCredentialType="Windows" proxyCredentialType="None"
          realm="" />
        <message clientCredentialType="Windows" negotiateServiceCredential="true"
          algorithmSuite="Default" establishSecurityContext="true" />
      </security>
    </binding>
  </wsHttpBinding>

client configuration

<wsHttpBinding>
            <binding name="WSHttpBinding_IService1" closeTimeout="00:10:00"
                openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00"
                bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
                maxBufferPoolSize="524288" maxReceivedMessageSize="2147483647"
                messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
                allowCookies="false">
                <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
                    maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
                <reliableSession ordered="true" inactivityTimeout="00:10:00"
                    enabled="true" />
                <security mode="Message">
                    <transport clientCredentialType="Windows" proxyCredentialType="None"
                        realm="" />
                    <message clientCredentialType="Windows" negotiateServiceCredential="true"
                        algorithmSuite="Default" establishSecurityContext="true" />
                </security>
            </binding>
        </wsHttpBinding>

Below mentioned is my client code

class Program
{
static void Main(string[] args)
{
    Program prog = new Program();
    Thread JavaClientCallThread = new Thread(new ThreadStart(prog.ClientCallThreadRun));
    Thread JavaStatusCallThread = new Thread(new ThreadStart(prog.StatusCallThreadRun));
    JavaClientCallThread.Start();
    JavaStatusCallThread.Start();
}

public void ClientCallThreadRun()
{
    XmlDocument doc = new XmlDocument();
    doc.Load(@"D:\t72CalculateReasonableWithdrawal_Input.xml");
    bool error = false;
    Service1Client Client = new Service1Client();
    string temp = Client.Processing(doc.OuterXml, ref error);
}

public void StatusCallThreadRun()
{
    int i = 0;
    Service1Client Client = new Service1Client();
    string temp;
    while (i < 10)
    {
        temp = Client.Status();
        Thread.Sleep(1500);
        Console.WriteLine("TotalTestScenarios;CurrentTestCase = {0}", temp);
        i++;
    }
}
}

Can any one please help.

Beygi
  • 1,918
  • 1
  • 16
  • 22
krishna555
  • 223
  • 5
  • 18
  • @Beygi ---- i have modified as you suggested. Please take a look at it. – krishna555 Jun 29 '12 at 18:53
  • Are you sure that your bindings are session enabled? Please provide both client and server configuration if not. Are both calls in one session? - Please provide client code. – Dmitry Harnitski Jun 29 '12 at 19:08
  • @DmitryHarnitski --- provided the complete additional info that you have requested. Please take a look – krishna555 Jun 29 '12 at 19:18
  • every new Client uses new sesson so Processing() is run in session1 and Status() is run in session2. Create one client object and use it for both methods – Dmitry Harnitski Jun 29 '12 at 19:35
  • @Dmitry Harnitski ---- the client is the same only single client will call both the methods. – krishna555 Jun 29 '12 at 19:39
  • You create client two times inside your code. Find this line - Service1Client Client = new Service1Client(); there should be only one new Service1Client(); – Dmitry Harnitski Jun 29 '12 at 19:40
  • @DmitryHarnitski ---- oh i see it now. let me modify it and see whetehr it works. – krishna555 Jun 29 '12 at 19:41

2 Answers2

1

First because you need to access service simultaneously, when service is processing the first client call(Processing), you need to change service concurrency mode to Multiple.

Also you want to maintain each client processing status, so you need to set instance context mode to the PerSession.

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode= InstanceContextMode.PerSession)]

Note

  • Default InstanceContextMode is PerSession
  • Default ConcurrencyMode is Single

You can do following to make sure your configuration is compatible with PerSession InstanceContextMode, using this method, WCF will throw a run-time exception if necessary

[ServiceContract(SessionMode=SessionMode.Required)]

Note With InstanceContextMode.PerSession you will get different instance per each proxy that you create

So you need only one instance of "Service1Client" per client, that you will call its Process method and also retrieve status from it.

Also for virtual heavy processing you can use Thread.Sleep(millisecond) for test proposes only in the "Processing" method(Service-Side).

For the client application if you want to call "Processing" method and then using the Status method to retrieve status, you need to call Process method Asynchronously.

1.Right click on the service reference in solution-explorer and choose "Configure Service Reference" then check the "Generate asynchronous operation" and press OK.

2.Change your client code like this

static void Main(string[] args)
{
    StartProcessing();
    StatusReport();

    Console.ReadLine();
}

static ServiceClient Client = new ServiceClient();
private static bool Completed = false;

public static void StartProcessing()
{
    XmlDocument doc = new XmlDocument();
    doc.Load(@"D:\t72CalculateReasonableWithdrawal_Input.xml");
    bool error = false;

    Client.ProcessingCompleted += Client_ProcessingCompleted;
    Client.ProcessingAsync(doc.OuterXml);

    Console.WriteLine("Processing...");
}

static void Client_ProcessingCompleted(object sender, ProcessingCompletedEventArgs e)
{
    // processing is completed, retreive the return value of Processing operation
    Completed = true;
    Console.WriteLine(e.Result);
}

public static void StatusReport()
{
    int i = 0;
    string temp;
    while (!Completed)
    {
        temp = Client.Status();
        Console.WriteLine("TotalTestScenarios;CurrentTestCase = {0}", temp);
        Thread.Sleep(500);
        i++;
    }
}
Beygi
  • 1,918
  • 1
  • 16
  • 22
  • @Beygi---- if i am doing only one instance of Service1Client per client then i am unable to get the threads working parallely. to check if i am getting the correct out puts. – krishna555 Jun 29 '12 at 21:12
  • @krishna555 'Update has been completed successfully.' :) – Beygi Jun 30 '12 at 10:51
  • Do i need do any code change on the Server(wcf service) side, for the above written client to work? – krishna555 Jul 02 '12 at 12:50
  • the client code that you have provided above is not doing the things parallely as it first calling the Processing and then waiting there until it is completed and then giving the final result of the Status method. – krishna555 Jul 02 '12 at 13:59
  • @krishna555 don't forget that you need to do all steps that i provided in this answer, not just the client code. – Beygi Jul 02 '12 at 15:17
  • i did every thing that you mentioned above. But i am getting the final results correctly but the asynchronous call is not working. As you said i checked the option for asynchronous and did all the rest of the stuff. Do you want me provide the service Contract interface on the service side? – krishna555 Jul 02 '12 at 16:01
  • @krishna555 can you please log events in console and tell me how are those methods being processed, the correct one is : "Processing...", then "TotalTestScenarios;CurrentTestCase = {0}" multiple times, and finally "Completed", add a console.writeline for last one in Client_ProcessingCompleted – Beygi Jul 02 '12 at 16:43
  • It is beeing processed in this way Processing... Results of the Processing method TotalTestScenarios;CurrentTestCase = {0} ..... just one is being printed – krishna555 Jul 02 '12 at 17:05
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/13317/discussion-between-beygi-and-krishna555) – Beygi Jul 02 '12 at 17:07
0

PerSession will not make your static variables not shared across object instances. The only thing that PerSession context mode does is controlling of object lifetime.

With PerSession WCF does not destroy service object until session is over. Session can be closed explicitly by client or by timeout (default is 10 min). Every next call from client with the same session Id will be routed by WCF to existing object.

Variables should be not static to prevent sharing across different service instances. State of variables will be maintened by WCF as long as you use InstanceContextMode.PerSession and binding that maintain session.

public int TotalTests = 0;
public int CurrentTest = 0;

I would also add SessionMode.Required to contract to make sure that service is properly configured.

 [ServiceContract(SessionMode = SessionMode.Required )]
Dmitry Harnitski
  • 5,838
  • 1
  • 28
  • 43