galets
galets

Reputation: 18482

WCF Callback Channel gets disposed prematurely?

My application is using the net.tcp WCF service with a callback channel. For some reason I'm not able to send callbacks on event. Here's what I'm doing (all code server-side):

On initialization:

OperationContext Context { get; protected set; }
...
Context = OperationContext.Current;

On event:

var callback = Context.GetCallbackChannel<IServiceCallbackContract>();
callback.SomeMethod();

This fails on SomeMethod() with following exception: {"Cannot access a disposed object.\r\nObject name: 'System.ServiceModel.Channels.ServiceChannel'."}

Apparently, something disposes callback channel, even though client still able to talk to server using direct (non-callback) channel. This is pretty weird. Which object am I supposed to hold on to in order to issue a callback? Is there a certain thread this must be running in?

Upvotes: 2

Views: 9653

Answers (5)

Gaz83
Gaz83

Reputation: 2875

I had a similar issue in my chat app. If I closed my app and then tried opening again I kept getting Cannot access a disposed object when opening the channel.

Tracing backwards through the app I did the following to hit the root cause.

  • Put a break point in the client app at when I do the final call to the service (log off) before closing. - Nothing to report here.
  • Remote Debugged the service on the server in the log off method. - Noticed the callback wouldn't work and generated the exception because the channel was disposed for some reason.
  • Checked the attributes for the OperationContract - Found the issue!

The problem was, and WCF experts do correct me if I am wrong, that the OperationContract was like so

[OperationContract(IsInitiating = false, IsTerminating = true)]
bool RemoveUser(Client user);  

Setting IsTerminating = true meant that the channel was closed before the service was processing the logoff method and so when it tried to callback it couldn't.

Setting IsTerminating = false and closing the channel on the client side sorted the problem for me.

In my app the callback shouldn't of even be happening buts thats a seperate issue all together and not related. The main point is that if you have IsTerminating = True on an operation then make sure you are not using a callback.

Upvotes: 0

galets
galets

Reputation: 18482

The issue has been resolved as my error. The callbacks worked as designed, except some were stale, which when called were throwing exceptions. Since I wasn't try/catching them, whole event was breaking on those stale callbacks :(.

Thanks to everyone who tried to answer my question.

Upvotes: 0

Philippe
Philippe

Reputation: 4051

Did you try to use OperationContext.Current directly instead of the instance variable Context?

Upvotes: 0

mundeep
mundeep

Reputation: 2747

I was running into similar problems where my service would make an async call to the Business layer an then wait for an event to fire back on the service. Whenver the event fired the Callback context was lost. I have not delved into the details of why this is but i ended up implementing a workaround of essentially storing a reference to the currentcontext and firing off a seperate Thread to call the to call the business layer and once it is complete fire the callback with the reference i have stored.

1) Create new class that would contain both my input request and a refence to the callback eg.

public struct MyCallbackDetails {
     public MyCallbackDetails(IMyServiceCallback callback, RequestType request) : this() 
         Callback = callback;
         Request = request;
}

public IMyServiceCallback Callback { get; set; }

public RequestType request { get; set; }
}

2) Then i would fire off a seperate thread passing a MyCallbackDetails object instead of just the request:

public ResponseType MyServiceMethod(RequestType request) {
    //...Do Some Stuff
    //Create MyCallbackDetails object to store reference to the callback and keep channel open
    MyCallDetails callDetails = new MyCallDetails(OperationContext.Current.GetCallbackChannel<IMyServiceCallback>(), request);
    //Fire off a new thread to call the BL and do some work
    Thread processThread = new Thread(RunCallbackMethod);
    processThread.Start(callDetails);
}

3) And my RunCallbackMethod would make the BL call and respond with a callback.

void RunCallBackMethod(Object requestDetails)
{
    //Use callbackdetails to make BL calls
    MyCallbackDetails callDetails = (MyCallbackDetails)requestDetails;
    // Make BL call - all code under here is syncrhonous
    ResponseType response = BusinessLayer.BusinessMethod(callDetails.Request);
    //NB: If your responsetype is a business object you will need to convert it to a service object
    callDetails.Callback.SomeMethod(results);
}

NB: Yes i have now done away with having an event from my Business Layer fire back to the Service Layer however as i am firing off a seperate thread for the Business Layer it still runs asynchronously and acts the same as if i was calling the BL directly in an ASync manner and waiting for an event to notify its completion.

PS: Thanks to Rory for the idea and most of the code to implement this.

Upvotes: 2

JP Alioto
JP Alioto

Reputation: 45117

Configure Tracing and see what exception is faulting your channel.

Upvotes: 4

Related Questions