Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WCF Dispose() Not Called With InstanceContectMode = PerSession

Tags:

.net

wcf

In PerSession how do I get Dispose() on the service to fire? In the code below Dispose() does not get called. Neither when I call .Close() nor if I let the session time out.

If I change the service to PerCall Dispose() is called (with each method call). With PerSession I am getting a session (tested with serviceStartTime).

Service

[ServiceBehavior (InstanceContextMode=InstanceContextMode.PerSession)]
public class MagicEightBallService : IEightBall, IDisposable
{
    private DateTime serviceStartTime;
    public void Dispose()
    {
        Console.WriteLine("Eightball dispose ... " + OperationContext.Current.SessionId.ToString());
    }
    public MagicEightBallService()
    {
        serviceStartTime = DateTime.Now;
        Console.WriteLine("Eightball awaits your question " + OperationContext.Current.SessionId.ToString() + " " + serviceStartTime.ToLongTimeString());
    }
    public string ObtainAnswerToQuestion(string userQuestion)
    {
        return "maybe " + OperationContext.Current.SessionId.ToString() + " " + serviceStartTime.ToLongTimeString();
    }

Client

    using (EightBallClient ball = new EightBallClient())
    {    
        while (true)
        {
            Console.Write("Your question: ");
            string question = Console.ReadLine();
            if (string.IsNullOrEmpty(question)) break;
            try
            {
                string answer = ball.ObtainAnswerToQuestion(question);
                Console.WriteLine("8-ball says: {0}", answer);
            }
            catch (Exception Ex)
            {
                Console.WriteLine("ball.ObtainAnswerToQuestion exception " + Ex.Message);
            }               
        }
        ball.Close();
     }

Service-Contract

[ServiceContract (SessionMode = SessionMode.Required)]
public interface IEightBall
{
    [OperationContract]
    string ObtainAnswerToQuestion(string userQuestion);

    [OperationContract]
    sDoc GetSdoc(int sID);

    DateTime CurDateTime();
}

Host

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="WSHttpBinding_ISampleService" 
                 closeTimeout="00:01:00" openTimeout="00:01:00" 
                 receiveTimeout="00:10:00" sendTimeout="00:10:00">
          <security mode="Message" />
          <reliableSession ordered="true"
                   inactivityTimeout="00:10:00"
                   enabled="true" />
        </binding>
      </wsHttpBinding>
    </bindings>
    <services>
      <service name="MajicEightBallServiceLib.MagicEightBallService"
               behaviorConfiguration="EightBallServiceMEXBehavior" >
        <endpoint address=""
                  binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ISampleService"
                  contract="MajicEightBallServiceLib.IEightBall">
        </endpoint>
        <endpoint address="mex"
                  binding ="mexHttpBinding"
                  contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8000/MagicEightBallService"/>
          </baseAddresses>
        </host>             
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="EightBallServiceMEXBehavior">
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

namespace MagicEightBallServiceHost
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("**** Console Based WCF Host *****");

            using (ServiceHost serviceHost = new ServiceHost(typeof(MagicEightBallService)))
            {
                serviceHost.Open();
                Console.WriteLine("The service is running");
                Console.ReadLine();
            }
        }
    }
}
like image 895
paparazzo Avatar asked Jun 14 '12 17:06

paparazzo


1 Answers

Dispose() method will be fired. The only question question is "When?".

Answer to that question depends on the service configuration.

There are several possible scenarios:

  1. Session is not supported by binding
  2. Normal session
  3. Reliable session

Dispose() is fired when session is closed for PerSession context mode. So we need to check how long does session live in different scenarios.

For some configurations (for example default BasicHttpBinding) session is not started at all. In case of session-less configuration PerCall and PerSession context modes have no difference and Dispose method will be called very soon after your main method executed.

When Session is Enabled it can be closed explicitly by client or by timeout. Normally it is controlled by client. Client initiates session before making first call to service and closes it when Client object is closed.

ServiceClient proxy = new ServiceClient();
Console.WriteLine(proxy.GetData(123));
proxy.Close();  

proxy.Close() method above closes the session in server what in turn executes Dispose().

Session management is a big performance driver because it requires additional calls between client and server to be performed.

So normally Dispose will be called when Client wants to close the session.

If client did not close session for any reason it will be closed by service host after certain period of time. That period is controlled by Binding.ReceiveTimeout property. Default value for that property is 10 min.

Session will be closed and (Dispose() fired) if nobody sent request to server with certain Session Id for 10 min. This default timeout can be changed by setting receiveTimeout to some shorter value in web.config.

<wsHttpBinding>
  <binding name="wsHttpEndpointBinding" receiveTimeout="00:00:05">
  </binding>
</wsHttpBinding> 

ReliableSession.InactivityTimeout is additionally checked when Reliable session is enabled. It is also defaulted to 10 min.

It works as expected in self-hosted and IIS-hosted services.

Try to update your client code as follows to test:

using (EightBallClient ball = new EightBallClient())
{    
    ball.ObtainAnswerToQuestion("test");
    ball.Close();
} 
like image 59
Dmitry Harnitski Avatar answered Sep 28 '22 01:09

Dmitry Harnitski