2011-09-03 23 views
9

Necesito crear un servicio que mantendrá una sesión WCF. En el constructor leo en los datos de la base de datos y cuando la sesión termina tengo que guardarlo de nuevo.Cuándo se llama a destructor en un servicio WCF

Si entiendo correctamente, la sesión finaliza cuando llamo a Close() en el cliente (Mi cliente ServiceClient se creó con SvcUtil.exe).

Cuando lo pruebo veo que a veces se lo llama después de aprox. 10 minutos, a veces después de 20 minutos y a veces nada.

Entonces, ¿cuándo se llama al destructor?

Servicio

[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> 

cliente

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

En primer lugar, se trata de un diseño de servicio completamente erróneo. –

Respuesta

16

De docs

El pr ogrammer no tiene control sobre cuándo se llama el destructor porque esto está determinado por el recolector de basura. El recopilador de basura busca objetos que ya no se utilizan en la aplicación . Si considera que un objeto es elegible para su destrucción, llama al destructor (si existe) y recupera la memoria utilizada para almacenar el objeto . Los destructores también son llamados cuando el programa sale.

Existe un problema con su implementación. Para persistir datos, está utilizando destructor. Esto es incorrecto porque los destructores no se pueden llamar de manera determinista, sino que se procesan en una cola de finalización separada. Esto significa que, aunque haya destruido el objeto, su destructor puede no ser llamado inmediatamente.

Cómo solucionar este
Retire el destructor y patrón de uso IDisposable lugar, poner guardar la lógica en botar. Una vez terminada la sesión, WCF llamará IDisposable.Dispose

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

EDIT
Pls también ver el comentario a esta respuesta. De hecho, estoy de acuerdo en que IDisposable no es el lugar adecuado para las confirmaciones de la base de datos, no se me había ocurrido antes. Además de las soluciones proporcionadas en el comentario, puede usar explicit session demarcation

+16

¡No! ¡No lo coloque en 'IDisposable.Dispose' tampoco! 'IDisposable.Dispose' es para limpiar los recursos gestionados. Guardar en una base de datos no es limpiar un recurso administrado. Esto va en contra del uso * aceptado * y * esperado * de esta interfaz. Haga que 'SetName' confirme los cambios en la base de datos, o proporcione otro método en el servicio' Commit'. Además, una pequeña objeción, en C# lo llamamos un "finalizador". Sí, la confusión abunda en este tema. – jason

Cuestiones relacionadas