0

I have a WCF service that I need to combine in an existing application.
Service's interface:

[ServiceContract(CallbackContract = typeof(IClientCallback))]
    public interface IExecutionService
    {
        [OperationContract]
        ExecutionStatus Execute(string path);

        [OperationContract]
        ExecutionStatus Abort();
    }

And callback:

[ServiceContract]
    public interface IClientCallback
    {
        [OperationContract(IsOneWay = false)]
        string GetUserInput(string message, string information, bool isPause);

        [OperationContract(IsOneWay = true)]
        void OutputAvailable(OutputAvailableEventArgs args);

        [OperationContract(IsOneWay = true)]
        void ResultAvailable(ResultAvailableEventArgs args);

        [OperationContract(IsOneWay = true)]
        void ExecutionStarted(ExecutionStartedEventArgs args);

        [OperationContract(IsOneWay = true)]
        void ExecutionEnded(ExecutionEndedEventArgs args);
    }

The service is performing some work and reports when there's something to report. Args classes are marked as DataContract with namespaces. They contain .Net native types (int, string, DateTime).

I created a testing application and everything works great (time from asking the service to execute until ExecutionStarted is called ~1 second).

When creating a client in my existing application, there are 2 issues:

  1. Slowness - about 1 minute from Execute() to ExecutionStarted().
  2. The last call - ExecutionEnded - just doesn't happen. What does happen? The last OutputAvailable call happens instead. During debug, I see the service calling the ExecutionEnded method, but what is invoked on the client side is OutputAvailable (which should have been called, only earlier and not instead of the finish method).

I'm using the same test in my test client and my real application.

Some code that might help, I hope:

Service:

class declaration:

  [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant)]
        public class ExecutionService : IExecutionService

public ExecutionStatus Execute(string path)
        {
            lock (locker)
            {
                var tempClient = OperationContext.Current.GetCallbackChannel<IClientCallback>();
                if (client == null)
                    client = tempClient;
                else if (client != null && client != tempClient)
                {
                    throw new FaultException(new FaultReason("Execution Service is busy processing a request."));
                }
                else if (tempClient == null)
                    throw new FaultException(new FaultReason("No client found, execution is aborted."));
                Log.InfoFormat("client: "+ DateTime.Now);
                Log.InfoFormat(client == null ? "null" : client.ToString());
            }
            ((IContextChannel)client).OperationTimeout = new TimeSpan(0,3,0);
            Task<ExecutionStatus> executionTask = Task.Factory.StartNew(() => HandleExecution(path));
            return executionTask.Result;
}

(Making sure I have only one client at a time. I need the reentrant mode so I can use callbacks during the service's methods.)

Client: Creating the connection to the service:

 var myBinding = new WSDualHttpBinding();
 var myEndpoint = new EndpointAddress("http://localhost:3050/ExecutionService");
 var myChannelFactory = new DuplexChannelFactory<IExecutionService>(this, myBinding, myEndpoint);
 service = myChannelFactory.CreateChannel();
 var executionStatus = service.Execute(@"C:\Users\nosh\Desktop\launcher.py"); 

I'll publish more code if necessary.
Another issue is that in my real app, I get:

A first chance exception of type 'System.TimeoutException' occurred in System.ServiceModel.dll
A first chance exception of type 'System.TimeoutException' occurred in mscorlib.dll

just before the 'execute' method returns, and later I get:

A first chance exception of type 'System.Runtime.Serialization.SerializationException' occurred in System.Runtime.Serialization.dll
A first chance exception of type 'System.ServiceModel.Dispatcher.NetDispatcherFaultException' occurred in System.ServiceModel.dll
A first chance exception of type 'System.ServiceModel.Dispatcher.NetDispatcherFaultException' occurred in System.ServiceModel.dll
A first chance exception of type 'System.ServiceModel.Dispatcher.NetDispatcherFaultException' occurred in System.ServiceModel.dll
A first chance exception of type 'System.ServiceModel.Dispatcher.NetDispatcherFaultException' occurred in System.ServiceModel.dll

This doesn't happen in my test application.
Any idea what's wrong here?

Edit
I think I know what's wrong, just not how to fix it. The problem is that my real app is WPF and it has some issues working with WCF. I tried switching to NetTcpBinding but it didn't help. I'd appreciate other suggestions.

Edit 2
For the WPF issue you should add the following in your callback implementation:

[CallbackBehavior(UseSynchronizationContext = false)]

(Thanks to Ian Ringrose for providing the answer here.)
Now I'm still facing a non responsive WPF client but the connection to the service doesn't hang. I'd appreciate more suggestions.

Edit 3
Now it's only a single method that isn't being called on the client. The connection is alive (I have other calls after it), the method isn't doing anything special.

Community
  • 1
  • 1
Noich
  • 14,631
  • 15
  • 62
  • 90

2 Answers2

0

I don't know how you set up your service, but do you use the async method for calling the service? otherwise I recommend that you implement that. Your service is static, so that could be the problem too, when you run it on a other device the link won't work. As for the serialization error I can't help.

machie27
  • 137
  • 1
  • 8
  • I'm not sure what you mean by Async method. I'm using the methods exposed in the service's interface. Also, not sure what you mean by static service (I'm new to `WCF`). Please see my edit. – Noich Mar 28 '13 at 08:38
  • soory I was not clear, I meant your link to the service is static, that could fix your last problem, as for the Asynchronous part, use this to access your web service. I found a link where the asynccallback is described [link]http://stackoverflow.com/questions/1047662/what-is-asynccallback – machie27 Mar 28 '13 at 14:43
0

I suppose everyone has to stumble upon those WTFs from time to time.
I can't explain the logic behind the solution, but the solution itself is simple enough:

  1. In the service solution, rename the problematic method (in the ICallback interface).
  2. Recompile and also update and recompile the problematic application.
  3. Run the application, the method is suddenly called.
  4. Rename the method again to the original name.
  5. Win!
Noich
  • 14,631
  • 15
  • 62
  • 90