【问题标题】:When is destructor called in a WCF service何时在 WCF 服务中调用析构函数
【发布时间】:2011-11-09 20:18:38
【问题描述】:

我需要创建一个维护 WCF 会话的服务。 在构造函数中,我从数据库中读取数据,当会话结束时,我必须将其保存回来。

如果我理解正确,当我在客户端上调用 Close() 时会话结束(我的客户端 ServiceClient 是使用 SvcUtil.exe 创建的)。

当我测试它时,我发现它有时会在大约 . 10 分钟,有时 20 分钟后,有时根本没有。

那么什么时候调用析构函数呢?

服务

   [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
   public class Service:IService
   {
     private User m_User = null;

     public  Service()
     {
       m_User = User.LoadFromDB();
     }

     ~Service()
     {
       m_User.SaveToDB();
     }

     public void SetName(string p_Name)
     {
       m_User.Name = p_Name;
     }
    }

Web.config

<?xml version="1.0"?>
<configuration>
  <system.web>
    <sessionState timeout="2" />
  </system.web>
  <system.serviceModel>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
      <services>
        <service name="Karatasi.Services.B2C"  behaviorConfiguration="ServiceBehavior">
          <host>
            <baseAddresses>
              <add baseAddress="http://localhost:19401/B2C.svc"/>
            </baseAddresses>
          </host>
        <endpoint
           address=""
           binding="wsHttpBinding"
           bindingConfiguration="test"
           contract="Karatasi.Services.IB2C"
         />
        <endpoint
           address="mex"
           binding="mexHttpBinding"
           contract="IMetadataExchange"
         />
       </service>
     </services>
   <bindings>
     <wsHttpBinding>
       <binding name="test" receiveTimeout="00:01:00" >
         <reliableSession enabled="true" ordered="false" inactivityTimeout="00:01:00"/>
       </binding>
     </wsHttpBinding>
    </bindings>
  <behaviors>
    <serviceBehaviors>
      <behavior name="ServiceBehavior">
        <serviceMetadata httpGetEnabled="true" />
        <serviceDebug includeExceptionDetailInFaults="false" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>
</configuration>

客户

    ServiceClient serviceClient = null;
    try
    {
      serviceClient = new ServiceClient();
      serviceClient.SetName("NewName");
      Console.WriteLine("Name set");
    }
    catch (Exception p_Exc)
    {
      Console.WriteLine(p_Exc.Message);
    }
    finally
    {
      if (serviceClient != null)
      {
        if (serviceClient.State == CommunicationState.Faulted)
        {
          serviceClient.Abort();
        }
        else
        {
          serviceClient.Close();
        }
      }
      Console.ReadKey();
    }

【问题讨论】:

  • 首先这是完全错误的服务设计。

标签: c# wcf session destructor


【解决方案1】:

来自docs

程序员无法控制何时调用析构函数 因为这是由垃圾收集器决定的。垃圾 收集器检查不再被 应用。如果它认为一个对象有资格销毁,它 调用析构函数(如果有)并回收用于存储的内存 物体。程序退出时也会调用析构函数。

您的实施存在问题。要持久化您正在使用析构函数的数据。这是错误的,因为无法确定地调用析构函数,它们在单独的终结队列中进行处理。这意味着即使您已销毁对象,也可能不会立即调用其析构函数。

如何解决此问题
删除析构函数并改用 IDisposable 模式,将保存逻辑放入 Dispose。一旦会话终止,WCF 将调用 IDisposable.Dispose

public class Service:IService, IDisposable
{
    public void Dispose()
    {
        //your save logic here
    }
}

编辑
请参阅对此答案的评论。我实际上同意IDisposable 不是数据库提交的合适位置,我以前没有想到。除了评论中提供的解决方案之外,您还可以使用explicit session demarcation

【讨论】:

  • 不!也不要放在IDisposable.Dispose 中! IDisposable.Dispose 用于清理托管资源。保存到数据库不会清理托管资源。这违背了该接口的接受预期用法。要么让SetName 提交对数据库的更改,要么在服务Commit 上提供另一种方法。另外,小问题,在 C# 中,我们称其为“终结器”。是的,这个话题很混乱。
猜你喜欢
  • 1970-01-01
  • 2011-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-04
  • 1970-01-01
  • 1970-01-01
  • 2018-09-07
相关资源
最近更新 更多