2011-09-03 17 views
9

Devo creare un servizio che manterrà una sessione WCF. Nel costruttore ho letto i dati dal DB e quando termina la sessione devo salvarlo di nuovo.Quando il distruttore chiama un servizio WCF

Se ho capito correttamente, la sessione termina quando chiamo Close() sul client (il mio client ServiceClient è stato creato con SvcUtil.exe).

Quando lo provo, vedo che a volte viene chiamato dopo ca. 10 minuti, a volte dopo 20 minuti ea volte no.

Quindi quando viene chiamato il distruttore?

Servizio

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

client

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

Prima di tutto, questo è un design di servizio completamente sbagliato. –

risposta

16

Da docs

Il pr ogrammer non ha alcun controllo su quando il distruttore viene chiamato perché questo è determinato dal garbage collector. Il raccoglitore spazzatura verifica la presenza di oggetti che non vengono più utilizzati dall'applicazione . Se considera un oggetto idoneo per la distruzione, lo chiama il distruttore (se presente) e recupera la memoria utilizzata per memorizzare l'oggetto . I distruttori vengono anche chiamati quando il programma termina.

C'è un problema con la vostra implementazione. Per mantenere i dati si utilizza il distruttore. Questo è sbagliato perché i distruttori non possono essere chiamati in modo deterministico, vengono elaborati in una coda di finalizzazione separata. Ciò significa che anche se hai distrutto l'oggetto, il suo distruttore potrebbe non essere immediatamente chiamato.

Come risolvere questo
Rimuovere il distruttore e utilizzare modello IDisposable, invece, mettere salvare la logica in Dispose. Una volta che la sessione è terminata, WCF chiamerà IDisposable.Dispose

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

EDIT
Pls vedere anche il commento a questa risposta. In realtà sono d'accordo che IDisposable non è il posto giusto per il commit di database, non mi è mai venuto in mente prima. In aggiunta alle soluzioni fornite nel commento è possibile utilizzare explicit session demarcation

+16

No! Non metterlo in 'IDisposable.Dispose' neanche! 'IDisposable.Dispose' è per la pulizia delle risorse gestite. Il salvataggio su un database non sta eliminando una risorsa gestita. Questo va contro l'uso * accettato * e * previsto * di questa interfaccia. O rendere 'SetName' commettere le modifiche al database, o fornire un altro metodo sul servizio' Commit'. Inoltre, cavillo minore, in C# lo chiamiamo un "finalizzatore". Sì, la confusione abbonda su questo argomento. – jason

Problemi correlati