WCF服务中析构函数什么时候被调用?

9
我需要创建一个服务来维护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();
    }

首先,这完全是错误的服务设计。 - Ladislav Mrnka
1个回答

16

引自文档

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

你的实现存在问题。你正在使用析构函数来持久化数据,这是错误的,因为析构函数无法确定地被调用,它们在单独的最终化队列中处理。这意味着即使你已经销毁了对象,它的析构函数可能不会立即被调用。

如何修复
删除析构函数并使用IDisposable模式,将保存逻辑放入Dispose方法中。一旦会话结束,WCF将调用IDisposable.Dispose。

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

编辑
请参阅对此答案的评论。我实际上同意IDisposable不是进行数据库提交的适当位置,在我之前没有想到这一点。除了评论中提供的解决方案之外,您还可以使用显式会话划分


17
不要将它放在 IDisposable.Dispose 中!IDisposable.Dispose 用于清理托管资源,将数据保存到数据库并不是清理托管资源。这与该接口的已接受预期用法相悖。要么使 SetName 将更改提交到数据库,要么在服务上提供另一个方法 Commit。此外,小小的争议,在 C# 中我们称其为“finalizer”。是的,关于这个话题存在混淆。 - jason

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接