2012-03-20 19 views
5

Tôi đã nhìn thấy lỗi này trên các bài đăng khác, nhưng không phải cho tình huống chính xác này.MessageQueue Disposed More Than Once

Tôi có hai lớp học thực hiện tương tự với MessageQueue. Do đó, tôi tóm tắt việc tạo và xử lý hàng đợi cho một lớp trợ giúp. Tôi nhận được lỗi này và tôi không thể xem hàng đợi có thể được xử lý nhiều lần như thế nào.

Object 'MessageQueue' có thể được xử lý nhiều hơn một lần trong phương pháp 'MsmqHelper.DisposeQueue (MessageQueue)'

Trong một trong những lớp học, đây là cách hàng đợi được sử dụng:

private MessageQueue _messageQueue; 

Sau đó, , trong constructor của lớp:

this._messageQueue = MsmqHelper.InitializeQueue(); 

Không phải là nó thực sự quan trọng, nhưng đối với sự hoàn chỉnh, đây là nơi mà các hàng đợi được sử dụng:

this._messageQueue.Send(workflowCreated); 

Và đây là những phương pháp Vứt bỏ:

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

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

    MsmqHelper.DisposeQueue(this._messageQueue); 
} 

Và đây là đoạn code trong lớp helper mà thực sự gọi Dispose():

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

ở đâu nó có thể cho hàng đợi được xử lý nhiều hơn một lần trong tình huống này?

** Chỉnh sửa **

Tôi nghĩ sẽ thật thú vị khi thêm nhận xét của tôi, trong cuộc trò chuyện bên dưới, tại đây. Đó là một bản tóm tắt tốt, cùng với câu trả lời được chấp nhận:

Tôi nghĩ mình đã hiểu ngay bây giờ. Tham số phương thức messageQueue không liên quan gì đến tham chiếu gốc (this._messageQueue) cho đối tượng. Vì vậy, kiểm tra messageQueue cho null, và thiết lập nó để null, không tốt. Người gọi vẫn có thể vượt qua trong biến của nó (this._messageQueue) ngay cả sau khi được xử lý. Do đó, có thể được xử lý nhiều hơn một lần.

Nhân tiện, thậm chí đặt biến của người gọi (this._messageQueue) thành null, trong phương thức gọi, không được trợ giúp. Vấn đề tồn tại chỉ trong MsmqHelper.DisposeQueue(). Vì vậy, câu trả lời là để vượt qua bởi ref hoặc đơn giản là không gọi DisposeQueue() và làm tất cả trong phương thức gọi.

** Chỉnh sửa 2 **

Sau khi thử điều này, tôi nhận được lỗi tương tự. Tôi chỉ đơn giản là không nhận được nó.

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

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

** Chỉnh sửa 3 - Lỗi? **

Tôi bắt đầu nghĩ rằng đây có thể là lỗi. Nếu tôi nhận xét messageQueue.Dispose(), lỗi sẽ biến mất. TUY NHIÊN, tôi có thể tin nhắn cuộc gọiQueue.Close() và messageQueue.Dispose() cùng nhau trong phương thức gọi. Đi con số. Tôi nghĩ rằng tôi sẽ thực hiện các cuộc gọi tương tự từ các phương thức gọi điện thoại hoặc chỉ gọi Close() hoặc Dispose() thay vì cả hai.

+0

nơi được xác định phương thức 'public void Dispose() (bool disposing)'? – Tigran

+0

Có thể là do 'Close' và' Dispose' thực hiện điều tương tự? –

+0

@Lasse Đó là một suy nghĩ thú vị. Tôi đã nhận xét dòng Close() và nó được biên dịch. Sau đó, tôi uncommented dòng Close(), và nhận xét dòng Dispose() và nó vẫn được biên dịch. Vì vậy, những gì bạn nói có thể là câu trả lời. Tôi có thể thề rằng tôi có cả hai dòng (Close() và Dispose()) trong mã trước và nó đã hoạt động. Tôi vừa kiểm tra, và tôi đã làm. Bây giờ tôi không chắc phải nghĩ gì. –

Trả lời

2

Đóng giải phóng mọi nguồn lực của đối tượng MessageQueue. Xem documentation here. Lỗi này rất có thể được tạo trong CA vì nó thấy rằng đường dẫn thực hiện của Close cũng gọi Dispose.

Từ các tài liệu:

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; 
    } 

Đóng dường như sẽ giải phóng tài nguyên nhưng sẽ cho phép các thành phần để reacquire họ nếu họ đã không được thu thập. Có thể thận trọng hơn khi mở đối tượng MessageQueue, sử dụng nó và sau đó đóng nó trong cùng một cuộc gọi thay vì mở nó trong một khoảng thời gian và đóng nó sau đó vì kết nối bộ nhớ đệm loại bỏ phí mở MessageQueue trong các cuộc gọi lặp lại.

* CẬP NHẬT * Dường như CA đối xử với CA2202 khác nhau cho các trường thành viên so với đi qua các đối tượng dùng một lần đến một phương pháp, thậm chí nếu phương pháp đó là tin tới lớp. Bất kể, theo tài liệu, bạn chỉ nên gọi Close() hoặc Dispose() nhưng không phải cả hai. Tuy nhiên, tôi khuyên bạn nên thay đổi thiết kế của mình để tạo, sử dụng và sau đó đóng đối tượng MessageQueue tất cả trong phạm vi hoạt động thư của bạn như ví dụ từ ví dụ tài liệu minh họa ở trên.

+1

Tôi cũng nghĩ vậy, nhưng không phải vậy. Xem Chỉnh sửa 3 của tôi ở trên. –

+0

Bạn có thể gọi cả hai thành công nhưng tại sao CA đang nắm trong một trường hợp và không có khác ngoài tôi. Điều đó đang được nói, bạn có thể nên mở MessageQueue, sử dụng nó, và đóng hoặc xử lý nó càng sớm càng tốt. Chỉ sử dụng nếu kịch bản của bạn yêu cầu khôi phục tài nguyên trước khi thu gom rác.Nếu không, kết nối bộ đệm ẩn sẽ cho phép một đối tượng MessageQueue tiếp theo được tạo/mở lại với chi phí thấp. – Jim

+1

Cảm ơn, Jim. Tôi chấp nhận câu trả lời của bạn vì điều này có ý nghĩa nhất về tình huống: "Có vẻ như CA xử lý CA2202 khác nhau đối với các trường thành viên so với truyền đối tượng dùng một lần cho phương thức, ngay cả khi phương thức đó là riêng tư cho lớp." –

2

Có.Điều này có thể vứt bỏ đối tượng nhiều lần:

Giá trị this._messageQueue đánh giá không không thay đổi sau khi gọi MsmqHelper.DisposeQueue(this._messageQueue).

Chỉ tham số địa phương (tên messageQueue) được gán giá trị null trong phương pháp DisposeQueue. Do đó, "null guard" không bảo vệ đúng thời gian tiếp theo. (Điều này là do hành vi mặc định của C# là Call-By-Value: vui lòng xem liên kết để hiểu ý nghĩa của điều này trong ngữ cảnh "chuyển giá trị của tham chiếu đến đối tượng".)

Hoặc tham gia ref hoặc chỉ định this._messageQueue = null trong người gọi.

+0

Ah, vì vậy tôi đã không được thiết lập hàng đợi để null, tôi đã thiết lập các biến (con trỏ đến đối tượng) để null. Và tôi chỉ làm như vậy cho tham số trong phương thức DisposeQueue(). Điều đó có nghĩa rằng this._messageQueue vẫn trỏ đến tham chiếu và không bao giờ được đặt thành null. Có đúng không? –

+0

Một * không bao giờ * đặt đối tượng thành null - chỉ các biến ;-) 'this._messageQueue' vẫn * đánh giá * đối tượng tương tự như trước đây, vâng. ("Đánh giá thành" có thể được đọc là "lưu trữ tham chiếu đến" khi nói về loại lớp.) –

+0

Lưu ý rằng trình biên dịch đang phàn nàn về phương thức 'DisposeQueue' và phương thức này sẽ chỉ xử lý đối tượng nhiều lần nếu thực sự * được gọi là * nhiều lần, nhưng khiếu nại là về chính phương thức đó, chứ không phải về nhiều cuộc gọi đến nó. Tôi nghĩ rằng nhiều khả năng cảnh báo là về 'Đóng' và 'Vứt bỏ' cả hai được gọi, mặc dù làm điều tương tự (ví dụ, xử lý đối tượng.) –

1

Nếu MessageQueue lớp thực hiện IDisposable iterface, sau đó không có điểm để sử dụng phương thức Dispose một cách rõ ràng và phương thức Close(), bởi vì trong tất cả các lớp học như vậy() phương thức Close thường là không phải là một phương pháp iterface mà là một phương pháp học . Thông thường, trong phương thức Dispose, tất cả các lệnh impementation đều phải gọi Close() mehod trước khi phát hành các tài nguyên quản lý/không được quản lý.

Một lần nữa, bằng cách đẩy mạnh trình trợ giúp tĩnh bên ngoài, bạn phá vỡ mẫu Dùng một lần. Nó không phải là cách chính xác để kiểm soát tuổi thọ của đối tượng; Bạn không cần phải mess lên với mẫu dùng một lần, bạn chỉ có thể sử dụng nó

Và mã của bạn có thể được đơn giản hóa như thế này:

// 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 và Jim: Cảm ơn bạn đã trả lời. Tôi sẽ thay đổi triển khai của tôi để sử dụng đề xuất của bạn. Trong khi câu trả lời của bạn không giải thích tại sao tôi gặp lỗi CA trong một trường hợp, nhưng không phải câu trả lời khác, câu trả lời của bạn vẫn hữu ích. Vấn đề là chính mã tương tự biên dịch trong một phương pháp, nhưng không phải là phương thức khác. –

Các vấn đề liên quan