2011-01-01 7 views
8

Sto usando il seguente codice:Che cosa potrebbe causare l'errore "Impossibile accedere a un oggetto disposto" in WCF?

private WSHttpBinding ws; 
private EndpointAddress Srv_Login_EndPoint; 
private ChannelFactory<Srv_Login.Srv_ILogin> Srv_LoginChannelFactory; 
private Srv_Login.Srv_ILogin LoginService; 

L'accesso è il mio costruttore:

public Login() 
     { 
      InitializeComponent(); 
      ws = new WSHttpBinding(); 
      Srv_Login_EndPoint = new EndpointAddress("http://localhost:2687/Srv_Login.svc"); 
      Srv_LoginChannelFactory = new ChannelFactory<Srv_Login.Srv_ILogin>(ws, Srv_Login_EndPoint); 
     } 

e sto usando il servizio in questo modo:

private void btnEnter_Click(object sender, EventArgs e) 
{ 
    try 
    { 

     LoginService = Srv_LoginChannelFactory.CreateChannel(); 
     Srv_Login.LoginResult res = new Srv_Login.LoginResult(); 
     res = LoginService.IsAuthenticated(txtUserName.Text.Trim(), txtPassword.Text.Trim()); 
     if (res.Status == true) 
     { 
      int Id = int.Parse(res.Result.ToString()); 
     } 
     else 
     { 
      lblMessage.Text = "Not Enter"; 
     } 
    } 
    catch (Exception ex) 
    { 
     MessageBox.Show(ex.Message); 
    } 
    finally 
    { 
     Srv_LoginChannelFactory.Close(); 
    } 
} 

Quando l'utente entra in un nome utente e password validi, tutto va bene. Quando l'utente immette un nome utente e una password errata, il primo tentativo viene visualizzato correttamente il messaggio "Not Enter", ma al secondo tentativo, l'utente vede questo messaggio:

{System.ObjectDisposedException: Cannot access a disposed object. 
Object name: 'System.ServiceModel.ChannelFactory`1[Test_Poosesh.Srv_Login.Srv_ILogin]'. 
    at System.ServiceModel.Channels.CommunicationObject.ThrowIfDisposed() 
    at System.ServiceModel.ChannelFactory.EnsureOpened() 
    at System.ServiceModel.ChannelFactory`1.CreateChannel(EndpointAddress address, Uri via) 
    at System.ServiceModel.ChannelFactory`1.CreateChannel() 

Come posso risolvere il mio codice per evitare che questo errore da verificarsi?

risposta

12

Srv_LoginChannelFactory.Close() è dove è stato smaltito. Quando chiami vicino, rinunci a qualsiasi risorsa non gestita che avevi. Se si tenta di fare qualcosa di diverso, controllarne lo stato o riaprirlo, l'eccezione "Impossibile accedere a un oggetto disposto".

Questo è vero ogni volta che si chiude un oggetto monouso e si prova a fare qualcosa con esso in seguito. Ad esempio, scrivendo su un file che è chiuso o eseguendo un'istruzione SQL su una connessione al database chiusa.

Per risolvere il problema sono disponibili tre opzioni.

  1. Non rendere Srv_LoginChannelFactory un campo. Invece renderlo locale al clic del pulsante. Se questo è l'unico posto in cui lo stai utilizzando, probabilmente ha senso farlo perché riduce la quantità di tempo in cui stai utilizzando una risorsa non gestita.

  2. Implementare IDisposable (si suppone di farlo ogniqualvolta si dispone di campo che è Usa e getta) non chiudere Srv_LoginChannelFactory tranne in Login.Dispose.

  3. Modificare il clic del pulsante per verificare lo stato di Srv_LoginChannelFactory prima di provare e creare un canale con esso. È comunque necessario implementare IDisposable nel caso in cui il clic del pulsante non avvenga.

Nota: EnsureOpened sembra che potrebbe essere utilizzato per controllare lo stato, ma funziona solo prima della sua apertura. Una volta chiuso sarà gettato.

Per quanto riguarda Chiudi() è uguale a Dispose.

Dalla sezione 'Personalizzazione di un Dispose Nome metodo' in Implementing Finalize and Dispose to Clean Up Unmanaged Resources negli orientamenti di progettazione per lo sviluppo di librerie di classi

Di tanto in tanto un nome specifico del dominio è più appropriato di smaltimento. Per esempio , un incapsulamento di file potrebbe voler utilizzare il nome del metodo Chiudi. In questo caso, implementa Dispose privatamente e crea un metodo di chiusura pubblica che chiama Dispose. Il seguente esempio di codice illustra questo schema.È possibile sostituire Chiudi con un nome metodo appropriato per il proprio dominio. Questo esempio richiede lo spazio dei nomi System.

L'idea è di dare parità al metodo Open. Personalmente ritengo che causi molta confusione, ma non riesco a pensare a qualcosa di meglio (CloseAndDispose?)

+0

Grazie @Conrad ma cosa strana è che utilizzare questo codice di stile su altre forme ma funzionano correctly.Why Close() metodo Dispose il mio oggetto ? È normale questo comportamento? – Arian

+1

sì, è normale. Chiudi e Disponi tipicamente fanno la stessa cosa. –

+0

I Non capisco veramente. Perché questo codice: LoginService = Srv_LoginChannelFactory.CreateChannel(); gettare un'eccezione? Considerare che lo stato è chiuso, creerò una nuova istanza e lo stato chiuso non è importante – Arian

2

Il problema qui (che penso abbia mancato a Conrad) è che Kerezo sta chiudendo ChannelFactory (Srv_LoginChannelFactory) che chiude (dispone) di tutti i suoi canali, quando probabilmente vuole chiudere solo il Canale (LoginService).

Così cambiano:

Srv_LoginChannelFactory.Close(); 

a:

try 
    { 
     LoginService.Close(); 
    } 
    catch 
    { 
     LoginService.Abort(); 
    }