【问题标题】:WCF Dispose() Not Called With InstanceContectMode = PerSession未使用 InstanceContectMode = PerSession 调用 WCF Dispose()
【发布时间】:2012-06-20 04:58:11
【问题描述】:

在 PerSession 中,如何让服务上的 Dispose() 触发?在下面的代码中 Dispose() 没有被调用。当我调用 .Close() 或让会话超时时都不会。

如果我将服务更改为 PerCall,则会调用 Dispose()(每个方法调用)。使用 PerSession 我得到一个会话(使用 serviceStartTime 测试)。

服务

[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();
    }

客户

    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();
     }

服务合同

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

    [OperationContract]
    sDoc GetSdoc(int sID);

    DateTime CurDateTime();
}

主持人

<?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();
            }
        }
    }
}

【问题讨论】:

  • 您的服务合同是否也指定了Session=true?我认为这是客户端知道在会话完成时告诉服务器的唯一方法。
  • @MichaelEdenfield 我应该把 Session=true 放在哪里?我试过 SessionMode = SessionMode.Required 但没有帮助。
  • [ServiceContract(Session = True)](我不知道这就是答案,我只知道我总是使用基于会话的服务这样做。)
  • @MichaelEdenfield 我尝试添加 Session = true 时遇到语法错误,但谢谢
  • 您是否尝试使用其他绑定,例如 NetTcpBinding? wsHttpBinding 仅支持具有安全性和可靠消息传递的会话

标签: .net wcf


【解决方案1】:

Dispose() 方法将被触发。唯一的问题是“什么时候?”。

该问题的答案取决于服务配置。

有几种可能的情况:

  1. 绑定不支持会话
  2. 正常会话
  3. 可靠的会话

Dispose()PerSession 上下文模式下关闭会话时触发。所以我们需要检查会话在不同场景下的存活时间。

对于某些配置(例如默认的BasicHttpBinding)会话根本没有启动。在无会话配置的情况下,PerCallPerSession 上下文模式没有区别,Dispose 方法将在您的 main 方法执行后很快被调用。

Session 启用时,它可以由客户端或超时显式关闭。通常它是由客户端控制的。客户端在第一次调用服务之前启动会话,并在客户端对象关闭时关闭它。

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

proxy.Close() 上面的方法关闭服务器中的会话,然后执行Dispose()

会话管理是一个很大的性能驱动因素,因为它需要在客户端和服务器之间执行额外的调用。

所以通常Dispose 会在客户端想要关闭会话时被调用。

如果客户端由于任何原因没有关闭会话,它将在一段时间后被服务主机关闭。该时间段由Binding.ReceiveTimeout 属性控制。 该属性的默认值为 10 分钟。

如果在 10 分钟内没有人向具有特定会话 ID 的服务器发送请求,会话将被关闭并(Dispose() 被触发)。 可以通过在 web.config 中将 receiveTimeout 设置为更短的值来更改此默认超时。

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

ReliableSession.InactivityTimeout 在启用可靠会话时额外检查。它也默认为 10 分钟。

它在自托管和 IIS 托管服务中按预期工作。

尝试更新您的客户端代码进行如下测试:

using (EightBallClient ball = new EightBallClient())
{    
    ball.ObtainAnswerToQuestion("test");
    ball.Close();
} 

【讨论】:

  • 请看我的代码。我打电话给 ball.Close();并且 Dispose() 没有触发。如果我让会话超时,则不会调用 Dispose()。是球.Close();没有关闭代理?
  • 我在发布答案之前对其进行了测试。我所说的一切都适用于 IIS 使用 *.svc 托管的服务。你如何托管你的代码?
  • 我托管在一个简单的控制台应用程序中。我将发布整个主机。谢谢
  • @Blam 我刚刚检查了控制台 - 一切正常。检查我的更新答案。您确定在测试时离开循环吗?关闭是仅在提出所有问题后才调用您的代码。
  • 试过了,还是没有 Dispose()。是的,我确信它正在离开循环并退出。就像我说的那样,我已经让它超时了。将其设置回 PerCall 并调用 Dispose()。我在 Vista 上。我可以在 Windows 2008 R2 上进行测试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-20
  • 2012-07-01
  • 2018-07-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多