2012-03-20 12 views
5

He visto este error en otras publicaciones, pero no para esta situación exacta.MessageQueue Disposed más de una vez

Tengo dos clases que hacen lo mismo con un MessageQueue. Por eso, resumí la creación y disposición de la cola a una clase de ayuda. Recibo este error y no veo cómo se puede eliminar la cola más de una vez.

Object 'MessageQueue' se puede disponer más de una vez en el método 'MsmqHelper.DisposeQueue (MessageQueue)'

En una de las clases, esto es cómo se utiliza la cola:

private MessageQueue _messageQueue; 

Entonces , en el constructor de la clase:

this._messageQueue = MsmqHelper.InitializeQueue(); 

No es que realmente importa, sino por la totalidad, aquí es donde se utiliza la cola:

this._messageQueue.Send(workflowCreated); 

Y aquí están los métodos Desechar:

public void Dispose() 
{ 
    Dispose(true); 
    GC.SuppressFinalize(this); 
} 

private void Dispose(bool disposing) 
{ 
    if (disposing == false) { return; } 

    MsmqHelper.DisposeQueue(this._messageQueue); 
} 

Y este es el código de la clase de ayuda que realmente llama a Dispose():

public static void DisposeQueue(MessageQueue messageQueue) 
{ 
    if (messageQueue != null) 
    { 
     messageQueue.Close(); 
     messageQueue.Dispose(); 
     messageQueue = null; 
    } 
} 

Donde es posible que la cola para ser eliminado más de una vez en esta situación?

** Editar **

pensé que sería bueno añadir mis comentarios, en la conversación a continuación, aquí. Es un buen resumen, junto con la respuesta aceptada:

Creo que lo entiendo ahora. El parámetro del método messageQueue no tiene nada que ver con la referencia original (this._messageQueue) al objeto. Por lo tanto, consultar MessageQueue para null, y establecerlo en null, no sirve de nada. La persona que llama aún podría pasar su variable (this._messageQueue) incluso después de haber sido eliminada. Por lo tanto, puede ser eliminado más de una vez.

Por cierto, incluso establecer la variable de la persona que llama (this._messageQueue) a null, en el método de llamada, no ayuda. El problema existe únicamente en MsmqHelper.DisposeQueue(). Entonces la respuesta es pasar por ref o simplemente no llamar a DisposeQueue() y hacerlo todo en el método de llamada.

** Editar ** 2

Después de probar esto, me sale el mismo error. Simplemente no lo entiendo

public static void DisposeQueue(ref MessageQueue messageQueue) 
{ 
    if (messageQueue == null) { return; } 

    messageQueue.Close(); 
    messageQueue.Dispose(); 
    messageQueue = null; 
} 

** Editar 3 - Bug? **

Estoy empezando a pensar que esto puede ser un error. Si digo messageQueue.Dispose(), el error desaparece. SIN EMBARGO, I puede llame a messageQueue.Close() y messageQueue.Dispose() juntos en el llamando al método. Imagínate. Creo que voy a hacer estas mismas llamadas desde los métodos de llamada, o solo llamar a Close() o Dispose() en lugar de a ambos.

+0

donde se define 'public void Dispose() (bool disposing)' method? – Tigran

+0

¿Podría ser posible porque 'Close' y' Dispose' hacen lo mismo? –

+0

@Lasse Esa es una idea interesante. Comenté la línea Close() y compilé. Luego descommenté la línea Close() y comenté la línea Dispose() y aún compilé. Entonces, lo que dijiste podría ser la respuesta. Podría haber jurado que tenía ambas líneas (Cerrar() y Deshacer()) en el código anterior y funcionó. Acabo de comprobar, y lo hice. Ahora no estoy seguro de qué pensar. –

Respuesta

2

Cerrar libera todos los recursos del objeto MessageQueue. Vea el documentation here. Es muy probable que el error se genere en CA porque ve que la ruta de ejecución de Cerrar también llama a Dispose.

De la documentación:

public void ReceiveMessage() 
    { 
     // Connect to the a on the local computer. 
     MessageQueue myQueue = new MessageQueue(".\\myQueue"); 

     // Set the formatter to indicate body contains an Order. 
     myQueue.Formatter = new XmlMessageFormatter(new Type[] 
      {typeof(String)}); 

     try 
     { 
      // Receive and format the message. 
      Message myMessage1 = myQueue.Receive(); 
      Message myMessage2 = myQueue.Receive(); 
     } 

     catch (MessageQueueException) 
     { 
      // Handle sources of any MessageQueueException. 
     } 

     // Catch other exceptions as necessary. 

     finally 
     { 
      // Free resources. 
      myQueue.Close(); 
     } 

     return; 
    } 

Cerrar parecer liberará los recursos, sino que permitirá que el componente de volver a adquirir ellos si no han sido recogidos todavía. Puede ser más prudente abrir el objeto MessageQueue, usarlo y luego cerrarlo dentro de la misma llamada en lugar de abrirlo durante un período de tiempo y cerrarlo más tarde porque el almacenamiento en caché de conexión elimina la sobrecarga de la apertura de MessageQueue en llamadas repetidas.

* ACTUALIZACIÓN * Parece que trata CA CA2202 de forma diferente para campos miembros frente pasando el objeto desechable a un método, incluso si ese método es privado a la clase. De todos modos, de acuerdo con la documentación, solo debe llamar a Cerrar() o Eliminar() pero no a ambos. Sin embargo, recomiendo cambiar su diseño para que pueda crear, usar y luego cerrar el objeto MessageQueue todo dentro del alcance de sus operaciones de mensaje, como se muestra en el ejemplo del ejemplo de documentación anterior.

+1

Pensé eso también, pero no es el caso. Ver mi Edit 3 arriba. –

+0

Puede llamar tanto con éxito, pero por qué CA se queja en un caso y no hay otro más allá de mí. Una vez dicho esto, es probable que debas abrir el MessageQueue, usarlo y cerrarlo o desecharlo lo antes posible. Use close solo si su escenario requiere readquirir el recurso antes de la recolección de basura.De lo contrario, el almacenamiento en caché de conexión permite crear/abrir nuevamente un objeto MessageQueue posterior con poca sobrecarga. – Jim

+1

Gracias, Jim. Estoy aceptando su respuesta porque tiene más sentido de la situación: "Parece que CA trata el CA2202 de forma diferente para los campos de miembros en comparación con pasar el objeto desechable a un método, incluso si ese método es privado para la clase". –

2

Sí.Esto puede disponer los objetos en múltiples ocasiones:

El valor que this._messageQueue evalúa como lo hace no cambio después de invocar MsmqHelper.DisposeQueue(this._messageQueue).

Sólo el parámetro local (nombrado messageQueue) fue asignado el valor null en el método DisposeQueue. Por lo tanto, la "protección nula" no protege correctamente los tiempos posteriores. (Esto se debe a que el comportamiento por defecto C# 's es Call-By-Value: por favor, consulte el enlace de entender lo que esto significa en el contexto de 'pasar el valor de una referencia a un objeto'.)

Puede tomar en ref o asignar this._messageQueue = null en la persona que llama.

+0

Ah, entonces no estaba configurando la cola en nulo, estaba estableciendo la variable (el puntero al objeto) en nulo. Y solo lo hice para el parámetro en el método DisposeQueue(). Eso significa que this._messageQueue todavía apuntaba a la referencia, y nunca se estableció en nulo. ¿Está bien? –

+0

Uno * nunca * establece objetos a nulos - solo variables ;-) 'this._messageQueue' todavía * evalúa * el mismo objeto que antes, sí. ("Evalúa a" se puede leer como "almacena la referencia" cuando se habla de tipos de clase). –

+0

Tenga en cuenta que el compilador se queja del método 'DisposeQueue', y este método solo eliminaría el objeto más de una vez si realmente * llamado * más de una vez, pero la queja se refiere al método en sí, no a llamadas múltiples. Creo que es más probable que la advertencia sea acerca de "Cerrar" y "Dispose", ambos llamados, a pesar de hacer lo mismo (es decir, deshacerse del objeto). –

1

Si MessageQueue clase implementa IDisposable iterface, entonces no tiene sentido utilizar el método Desechar de forma explícita y método Close(), ya que en todas esas clases método Close() es por lo general no es un método iterface sino más bien un método de clase . Normalmente, en el método Dispose toda la impedancia correcta debe llamar a Close() mehod antes de liberar los recursos gestionados/no gestionados.

De nuevo, al impulsar el auxiliar estático externo, se rompe el patrón Desechable. No es la forma correcta de controlar la vida del objeto; No es necesario echar a perder con el patrón desechable, sólo tiene que utilizar

Y su código puede simplificarse así:

// 1. Use static class. By the agreement, all helper classes should be static to avoid 
    // IDisposable inheritance, in example 
    public static class MsmqHelper//: IDisposable 
    { 
     //private MessageQueue _messageQueue; 

     //public MessageQueueHelper(bool workflowCreated) 
     //{ 
     // this._messageQueue = MsmqHelper.InitializeQueue(); 
     // this._messageQueue.Send(workflowCreated); 
     //} 

     public static SendMessage(object workflowCreated) 
     { 
      // 2. If static method in static class does not takes parameters, 
      // I might be better to to implicitly call the constructor? 

      // using(MessageQueue msmsq = MsmqHelper.InitializeQueue()) 

      using(MessageQueue msmsq = new MessageQueue()) 
      { 
       msmq.Send(workflowCreated); 
       msmq.Close(); 

       // MsmqHelper.DisposeQueue(msmq); 

       // 3. You should explicitly call Close object to immediately release  
       // unmanaged resources, while managed ones will be released 
       // at next GC rounds, as soon as possible 
      } 
     } 
     //private MessageQueue _messageQueue; 

     //public void Dispose() 
     //{ 
     // Dispose(true); 
     // GC.SuppressFinalize(this); 
     //} 

     //private void Dispose(bool disposing) 
     //{ 
    // if (disposing == false) { return; } 
    // 
    // MsmqHelper.DisposeQueue(this._messageQueue); 
    //} 

    //public static void DisposeQueue(MessageQueue messageQueue) 
    //{ 
    // if (messageQueue != null) 
    // { 
    //  messageQueue.Close(); 
    //  messageQueue.Dispose(); 
    //  messageQueue = null; 
    // } 
    //} 
} 
+0

Artur y Jim: gracias por sus respuestas. Cambiaré mi implementación para usar su sugerencia. Si bien sus respuestas no explican por qué obtendría el error de CA en un caso, pero no en el otro, sus respuestas siguen siendo útiles. El problema es que el mismo código exacto se compila en un método, pero no en el otro. –

Cuestiones relacionadas