WCF callback channel disconnects ahead of time?
My application is using the WCF net.tcp service with a callback channel. For some reason I am unable to send callbacks to the event. This is what I am doing (whole backend server):
On initialization:
OperationContext Context { get; protected set; }
...
Context = OperationContext.Current;
In an event:
var callback = Context.GetCallbackChannel<IServiceCallbackContract>();
callback.SomeMethod();
This does not work SomeMethod()
with the following exception:{"Cannot access a disposed object.\r\nObject name: 'System.ServiceModel.Channels.ServiceChannel'."}
Something appears to have a callback channel, although the client is still able to talk to the server using a forward (not backward) channel. This is pretty weird. Which object should I stick to in order to execute the callback? Is there a specific thread that it should work on?
a source to share
The problem was resolved as my mistake. The callbacks worked the way they were designed, with the exception of some of them being deprecated and throwing exceptions when called. Since I didn't try to catch them, all these events were split into all events: (.
Thanks to everyone who tried to answer my question.
a source to share
I was having similar issues where my service would make an asynchronous call to the business layer and then wait for the event to return to the service. When the event was fired, the callback context was lost. I didn't go into the details of why this is, but I ended up implementing a workaround essentially storing a reference to the current context and letting go of a separate thread to invoke the call to the business layer, and after it's done, the callback with the link I saved.
1) Create a new class that will contain both my input request and the confirmation for 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 disable the separate thread passing the MyCallbackDetails object, not 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 will make a 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, now I am done with the event from my work layer returning to the service layer again, but since I fire a separate thread for the business layer, it still runs asynchronously and acts the same as if I were calling BL directly in ASync mode and waiting for an event to signal its completion.
PS: Thanks to Rory for the idea and most of the code to implement this.
a source to share
I had a similar problem in my chat app. If I closed my app and then tried to open it again, I kept getting Cannot access a disposed object
when I opened the channel.
Tracking back through the app I did the following to hit the root cause.
- Place a breakpoint in the client application when I complete the service call (logout) before closing. - Nothing to report here.
- Remotely debug the service on the server in the logout method. - Noticed that the callback was not working and was throwing an exception because the channel was deleted for some reason.
- Attributes for OperationContract checked - problem found!
There was a problem and WCF experts corrected me if I am wrong that OperationContract was this way
[OperationContract(IsInitiating = false, IsTerminating = true)]
bool RemoveUser(Client user);
The setting IsTerminating = true
means the channel was closed before the service processed the logout method, and so when it tried to execute the callback, it failed.
Configuring IsTerminating = false
and closing the channel on the client side sorted out the issue for me.
In my application, the callback shouldn't even happen, but it's a separate issue all together and not related. The main point is that if you have IsTerminating = true
an activity on, make sure you are not using a callback.
a source to share