WCF method does not return - c#

I have a curious problem with a WCF service I've not been able to solve.
When I call a specific method on my service, it simply does not return to the client until the binding times out, although it already has returned on the server and a return message has been sent to the client.
I'm using ChannelFactory<T> on the client side, and shared interfaces that are defined like so:
[ServiceContract]
interface IService {
[OperationContract] bool Upload(Guid id, string name, byte[] data);
[OperationContract] bool Unlock(Guid id);
}
[ServiceContract(Name = "IService")]
interface IServiceAsync : IService {
[OperationContract] Task<bool> UploadAsync(Guid id, string name, byte[] data);
[OperationContract] Task<bool> UnlockAsync(Guid id);
}
The IService interface is implemented by the server class, the IServiceAsync interface is only used on the client (and works automagically).
The server is implemented in a standalone console application using ServiceHost, the server class looks like this:
[ServiceBehavior(InstanceContextMode = Single, ConcurrencyMode = Multiple)]
static class Server : IService {
public bool Upload(Guid id, string name, byte[] data) { ... }
public bool Unlock(Guid id) { ... }
}
On the client side, I create a channel using a ChannelFactory<T> like this:
static IServiceAsync GetService() {
EndpointAddress endpoint = new EndpointAddress(/* some Uri */);
NetTcpBinding binding = /* NetTcpBinding without security */;
ChannelFactory<IServiceAsync> factory =
new ChannelFactory<IServiceAsync>(binding, endpoint);
factory.Open();
IServiceAsync proxy = factory.CreateChannel();
((IServiceChannel)proxy).Open();
return proxy;
}
In my scenario, I then upload some files to the server, an operation that can be canceled by the user at any time. The code for the upload looks something like this:
static async void UseService(IServiceAsync proxy) {
try {
byte[] buffer;
do {
cancellationToken.ThrowIfCancellationRequested();
buffer = GetBytes();
bool uploaded = await proxy.UploadAsync(id, name, buffer);
} while(buffer.Length > 0);
((IClientChannel)proxy).Close();
}
catch (Exception ex) {
try {
proxy.Unlock(id);
}
catch {
// Connection may have failed
}
throw;
}
}
Now when the upload finishes everything works fine, but when the upload is canceled, and cancellationToken.ThrowIfCancellationRequested() throws an exception, I want to unlock the server.
So I call proxy.Unlock(id), which gets called on the server, returns there (confirmed by writing to the console immediately before return true; on the server), but does not return on the client side until the binding times out.
I looked at the network traffic once and confirmed that there is actually a return message being sent to the client. (I don't actually know whether proxy.Unlock(id) just returns or throws a CommunicationException, i suspect the second one.)
TL;DR: A WCF method does not return on the client side although it already returned on the server side, other methods work fine.
So, in case you're still here after that wall of text: what is wrong here?
How can I debug or solve this?
If you need any more information please let me know.
Update:
Thanks to Daniel's comment I did some tracing and managed to reproduce the problem on a small scale.
The problem only seems to happen when ConfigureAwait(false) is used when awaiting async WCF methods, an exception is thrown, and there is no context switch before the next call to a WCF method. I uploaded a small test project on GitHub.
Could this be a bug in the automagically implemented async methods in the WCF proxy?

Related

Communication between Topshelf service (acting as TCP server) and selfhosted OWIN WebAPI

I have a Topshelf windows service that acts as a TCP server. Inside this service, I also have a self-hosted (OWIN) WebAPI.
My goal is to somehow allow the WebAPI to communicate with the TCP server that's contained and running in the same service. Naturally I could simply use something like a "trigger" file or a shared DB that could be polled frequently, though I'd like to know of any more optimal/native ways to achieve this.
To get a better idea of the project, think of a single page application consuming my API and making certain calls with arbitrary string parameters. This data should then be passed to clients (C++ console apps using winsock) that are connected to the running TCP server.
The following Container is instantiated and passed to the Topshelf HostConfigurator
class ContainerService
{
private APIService _apiService;
private EngineService _engineService;
protected IDisposable WebAppHolder { get; set; }
public bool Start(HostControl hostControl)
{
var host = hostControl;
_apiService = new APIService();
_engineService = new EngineService();
// Initialize API service
if (WebAppHolder == null)
{
WebAppHolder = _apiService.Initialize();
}
// Initialize Engine service
_engineService.Initialize();
return true;
}
public bool Stop(HostControl hostControl)
{
// Stop API service
if (WebAppHolder != null)
{
WebAppHolder.Dispose();
WebAppHolder = null;
}
// Stop Engine service
_engineService.Stop();
return true;
}
}
Standard Topshelf stuff in program entry point (as mentioned above):
HostFactory.Run(hostConfigurator =>
{
hostConfigurator.Service<ContainerService>(containerService =>
{
containerService.WhenStarted((service, control) => service.Start(control));
containerService.WhenStopped((service, control) => service.Stop(control));
});
hostConfigurator.RunAsLocalSystem();
hostConfigurator.SetServiceName("Educe Service Host");
hostConfigurator.SetDisplayName("Communication Service");
hostConfigurator.SetDescription("Responsible for API and Engine services");
});
TCP Server:
public void Initialize()
{
_serverListener = new TcpListener(new IPEndPoint(hostAddress, (int)port));
_serverListener.Start();
_threadDoBeginAcceptTcpClient = new Thread(() => DoBeginAcceptTcpClient(_serverListener));
_threadDoBeginAcceptTcpClient.Start();
}
...
public void DoBeginAcceptTcpClient(TcpListener listener)
{
while(!_breakThread)
{
// Set the event to nonsignaled state.
TcpClientConnected.Reset();
// Start to listen for connections from a client.
Console.WriteLine("Waiting for a connection...");
// Accept the connection.
listener.BeginAcceptTcpClient(DoAcceptTcpClientCallback, listener);
// Wait until a connection is made and processed before continuing.
TcpClientConnected.WaitOne();
}
}
// Process the client connection.
public void DoAcceptTcpClientCallback(IAsyncResult ar)
{
// Get the listener that handles the client request.
TcpListener listener = (TcpListener)ar.AsyncState;
// End the operation and display the received data on the console.
Console.WriteLine("Client connection completed");
Clients.Add(listener.EndAcceptTcpClient(ar));
// Signal the calling thread to continue.
TcpClientConnected.Set();
}
WebAPI Controller:
public class ValuesController : ApiController
{
// GET api/values/5
public string Get(int id)
{
return $"Foo: {id}";
}
}
As mentioned earlier, what I seek is "communication" between the WebAPI and the windows service. How can I pass the "id" parameter from the WebAPI call to the _engineService object in my windows service? Perhaps something similar to WPF's MVVM Light Messenger? The idea is that it would then be parsed and sent to the appropriate TcpClient that is stored in the Clients List.
Any advice on how to achieve this will be appreciated. Please feel free to ask for clarification/more code.
Did you find any answer to your issue yet ?
I don't quite understand what you try to achieve looking for a communication between the two of them ? Do you want to somehow rely on TCP/IP to relay this id or in-memory ?
Potentially, you could consider a Mediator pattern and use this kind of library that seems quite useful in the case I understood : https://github.com/jbogard/MediatR
In a simpler approach, I would rely on events to achieve what you are trying to do, which is having a reactive communication from the HTTP request to the c++ users.
Did I understand you needs ? I am quite curious about the solution
I'm assuming you are trying to take an HTTP GET request's ID parameter and send it to TCP clients who are connected to the EngineService. If your EngineService is initialized before your ApiService, I think this is a question of how to get a handle to the one-and-only EngineService instance from within an ApiService's controller instances.
If I'm following you, you could make the EngineService a public static property of your ContainerService and reference it as ContainerService.EngineService from the controller (or anywhere in the app for that matter) or better register your EngineService as a singleton in a DI container an inject it into the ApiService.
Solution (calls to WebAPI trigger EngineService)
I now use RabbitMQ/EasyNetQ to achieve communication between the WebApi and the EngineService object containing my TCP clients.
I have incidentally split them into two separate Projects/Topshelf services now.
The following is the new "communication" component and it is instantiated in the EngineService constructor.
public class Communication
{
private readonly Logger _logger;
private readonly IBus _bus;
public delegate void ReceivedEventHandler(string data);
public event ReceivedEventHandler Received;
protected virtual void OnReceive(string data)
{
Received?.Invoke(data);
}
public Communication()
{
_logger = new Logger();
_bus = RabbitHutch.CreateBus("host=localhost", reg => reg.Register<IEasyNetQLogger>(log => _logger));
SubscribeAllQueues();
}
private void SubscribeAllQueues()
{
_bus.Receive<Message>("pipeline", message =>
{
OnReceive(message.Body);
});
}
public void SubscribeQueue(string queueName)
{
_bus.Receive<Message>(queueName, message =>
{
OnReceive(message.Body);
});
}
}
An event handler is then added.
This means that as soon as a message arrives to the bus, the data will be relayed to the event handler which will subsequently relay it to the first connected TCP client in the list.
public void Handler(string data)
{
//Console.WriteLine(data);
_clients[0].Client.Send(Encoding.UTF8.GetBytes(data));
}
...
_comPipe.Received += Handler;
And finally on the WebApi's controller:
public string Get(int id)
{
ServiceCom.SendMessage("ID: " + id);
return "value";
}
ServiceCom class. Allows sending a string message on the bus.
public static class ServiceCom
{
public static void SendMessage(string messageBody)
{
var messageBus = RabbitHutch.CreateBus("host=localhost");
messageBus.Send("pipeline", new Message { Body = messageBody });
}
}
Now that this is done, I am now looking to implement a way for the connected TCP clients to trigger updates/events in an additional SPA project that will act as a Portal / Client Management App.
My approach will probably make use of KnockOut.js and SignalR to achieve dynamic Views where TCP client events are displayed immediately and similarly actions on to WebAPI will trigger events in the TCP clients. I know it sounds like a bizarre combination of processes but it is all according to plan and working out as expected :)

Ping replies of a WCF service while busy with long running operations

I'm developing a client/server application using WPF and WCF.
The server application hosts a WCF service that is in charge to execute clients requests and callback them when something occurs.
The service interface define a duplex callback contract with all OneWay operations.
(simplified) IService
[ServiceContract(CallbackContract = typeof(ISrvServiceCallback))]
public interface ISrvService
{
[OperationContract(IsOneWay = true)]
void Ping();
[OperationContract(IsOneWay = true)]
void LongRunningOperation();
}
public interface ISrvServiceCallback
{
[OperationContract(IsOneWay = true)]
void PingReply();
[OperationContract(IsOneWay = true)]
void LongRunningOperationStatus(string reply);
}
The service needs to mantain some objects that change states according to clients calls. For this reason I decided to have a singleton service.
(simplified) Service
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class SrvService : ISrvService
{
MyObject statusObject;
public void LongRunningOperation()
{
//calling back the client reporting operation status
OperationContext.Current.GetCallbackChannel<ISrvServiceCallback>()
.LongRunningOperationStatus("starting long running application");
statusObject.elaborateStatus();
//calling back the client reporting object status
OperationContext.Current.GetCallbackChannel<ISrvServiceCallback>()
.LongRunningOperationStatus("object status: " + statusObject.ToString());
}
public void Ping()
{
OperationContext.Current.GetCallbackChannel<ISrvServiceCallback>().PingReply();
}
public SrvService()
{
statusObject= ...statusObject init...
}
}
As you can see I have a Ping operation exposed by the service that a client calls (every 5 seconds) to check if the server application is available on the network (each client has a server connectivity icon with red=server not available, green=server not available).
When a client requests a long running operation, the server starts working on that operation and can't reply to the ping requests (the client's server connectivity icon turns red).
Once the long running operation finishes, the server replies to all the requests made by the client and the server connectivity icon turns back green).
I would like to find a way to develop the service so the server always replies to the ping requests, also when a long operation is running.
How can I do it considering that i need to keep
InstanceContextMode.Single to mantain the state of the objects of
the service?
Are there other/better ways to ping a WCF service
availability and visually display the result on the client?
With a singleton service you're going to need a multi threaded implementation of your server instance to get the desired behavior, at the very least you'll need to run LongRunningOperation on a separate thread. If this operation is inherently not thread safe, you'll need to guard against multiple concurrent calls to it specifically with a lock or semaphore, etc in the implementation. This way when a client calls LongRunningOperation(), it executes in a separate thread and is free to respond to ping requests.
There are many ways to implement this. By the way you worded your question the client seems to be making asynchronous calls (as it appears to be making ping requests while waiting for LongRunningOperation to return) - so I'm also going to assume you have some knowledge of asynchronous programming. WCF has some built in ways of handling concurrency, but most of the documentation does not cover singleton instances so you're going to need to read carefully and focus on that special case.
I've had the most success with the async/await pattern (see here and here) - Once this was set up properly I had a very reliable and predictable pattern for long running service calls in a stateful singleton service.
Also, as far as pings are concerned you do point out that you are simply displaying the connectivity status for the user, but if you had plans to use it for control (checking if the service is online before making a call) there is a lot of discussions here on why you should avoid it.
EDIT: Quick example with async/await
[ServiceContract]
public interface ISrvService()
{
[OperationContract]
bool Ping(); // doesnt need to be async
[OperationContract]
Task<string> LongRunningOperation();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class SrvService : ISrvService
{
MyObject statusObject;
public async Task LongRunningOperation()
{
// lock/semaphore here if needed
await Task.Run(() => statusObject.elaborateStatus()); // you could impliment elaborateStatus() as an async Task method and call it without Task.Run
return statusObject.ToString();
}
public bool Ping()
{
return true;
}
public SrvService()
{
statusObject= ...statusObject init...
}
}
public class SrvClient : ClientBase<ISrvService>
{
public async Task<string> LongRunningOperation()
{
return await base.Channel.LongRunningOperation();
}
public async Task<bool> Ping()
{
// note that we still call this with an await. In the client we are awaiting the wcf service call
// this is independent of any async/await that happens on the server
return await Task.Run(() => base.Channel.Ping());
}
}
Using the client:
public class SomeApplicationClass()
{
SrvClient Client;
DispatcherTimer PingTimer;
public SomeClass()
{
BasicHttpBinding binding = new BasicHttpBinding();
EndpointAddress endpoint = new EndpointAddress(
"http://...:8000/Service/Address");
OutpostClient = new OutpostRemoteClient(binding, endpoint);
// pingTimer setup
}
// async voids are scary, make sure you handle exceptions inside this function
public async void PingTimer_Tick()
{
try
{
await Client.Ping();
// ping succeeded, do stuff
}
catch // specify exceptions here
{
// ping failed, do stuff
}
}
public async Task DoTheLongRunningOperation()
{
// set busy variables here etc.
string response = await Client.LongRunningOperation();
// handle the response status here
}
}
Also this answer seems relevant.

Logging request and response in WCF

I wanted to understand whether there are any better way to do logging or error handling in customized way with WCF
Here is the scenario.
I have a service as below
namespace IntegrationServices.Contract.SomeServices
{
[ServiceContract(Name = "SomeServices")]
public interface ISomeService
{
//Having 30+ contracts below is one of them
[OperationContract]
[WebInvoke(UriTemplate = "/GetOnlineSomething")]
SomeTransactionResponse GetOnlineSomething(string someNumber);
}
}
Which is implemented by below calss
namespace IntegrationServices.Service.PaymentServices
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
[GlobalErrorBehaviorAttribute(typeof(GlobalErrorHandler), Project.Name)]
public class PaymentService : ISomeService
{
public OnlinePaymentTransactionResponse GetOnlinePaymentTransaction(string someNumber)
{
//we have authentication code here which is OK
//Logging the request
_transactionKey = Guid.NewGuid();
TransactionRequest(/*some message and some parameter*/);
try
{
//do something
}
catch (Exception ex)
{
LogHelper.WriteErrorLogAsync(/*logging some more information*/);
response.ErrorMessage = Project.PHAPICommonErrorMessage;
}
//Logging the response
TransactionResponse(/*some parameter and error message from catch block*/);
return response;
}
}
}
Logging Function is as below
private void TransactionRequest(string xmlObject, Guid? groupKey, string name)
{
//writing to DB
}
private void TransactionResponse(string xmlObject, Guid? groupKey, string name)
{
//writing to DB
}
Now my question here is, I have to write in all 30+ function to log request and response like above.
Can anybody help me to how I can improve above or need to redesign whole approach.
I've had great success with using PostSharp for logging in my code bases. In the context of WCF its similar to the IServiceBehavior approach suggested by Aleksey L in that it gives you "hooks" that execute before and after the method's execution in which you can add your logging. The benefit comes in that you can also use the PostSharp logging attribute outside the context of WCF call.

Cannot Update WCF Service Reference after adding a method that returns an array or list

I am trying to test a WCF web service and am having problems when I introduce complicated return types. I have tried to keep this simple to start and am verifying I can pass data between the web service and a client. I have created the following files: MyService.svc, MyService.cs, and IMyService.cs. This is done in ASP.NET C#.
MyService.cs looks like this:
public class MyService : IMyService
{
public void DoWork()
{
}
public bool BoolTest()
{
return false;
}
public string StrTest(string input)
{
return input;
}
}
IMyService.cs Looks like this:
[ServiceContract]
public interface IMyService{
[OperationContract]
void DoWork();
[OperationContract]
bool BoolTest();
[OperationContract]
string StrTest(string input);
}
I have a console application which I connect to the Service above with a "Service Reference". When I use the code shown above, everything links up okay and I am able to make calls to the functions provided by the web service.
My problem occurs when I try to return anything more complicated such as an array, a list, or a custom object. After I add a function to the Web Service with one of these return types, then attempt to update the Service Reference in the Console Application, the reference fails to update and is lost.
Here is MyService.cs with the additional method that causes the failure:
public class MyService : IMyService
{
public void DoWork()
{
}
public bool BoolTest()
{
return false;
}
public string StrTest(string input)
{
return input;
}
public string [] StrArrTest()
{
string[] s = new[] { "test", "test2" };
return s;
}
}
Here is a copy of IMyService.cs with the additional method added that causes the error:
[ServiceContract]
public interface IMyService{
[OperationContract]
void DoWork();
[OperationContract]
bool BoolTest();
[OperationContract]
string StrTest(string input);
[OperationContract]
string [] StrArrTest();
}
If I use a List<string> as a return type or MyObject as a return type it also fails.
I am guessing there is some sort of serialization needed but am having a hard time tracking down exactly what I need to do.
Additional bits of information that might be helpful... The client and host are running on the same machine. When I access http://localhost/MyService.svc in a web browser there aren't any errors shown. When I access http://localhost/MyService.svc?wsdl a descent sized xml document appears.
Can anyone help explain what I need to do so I can return arrays and lists of objects in the web service?
I found this similar question, why does my silverlight reference to my wcf service blow up when I add a method to the wcf service that returns a generic list, but it applies to SilverLight which I am not using. I took a look at the Reference.cs file mentioned in the post, when everything is working it is populated with a bunch of stuff. When the reference is not updated correctly (after the method that returns an array is added), the Reference.cs file is empty with the exception of a comment about the file being auto-generated.

Catching " 'System.ServiceModel.EndpointNotFoundException' Exception happening during asynchronous web service call

I am creating an application that consumes a SOAP web service in C#. I generated a proxy class for the web service WSDL using the svcutil tool.
I added the proxy class to my code and I am using it to make calls to the web service and get results asynchrounsly.
Everything works pretty fine when the client has an Internet access. However, if I run attempt to access while the application doesn't have Internet access it crashes raising the following exception:
An exception of type 'System.ServiceModel.EndpointNotFoundException' occurred in
System.ServiceModel.Internals.dll but was not handled in user code
I am trying to catch this exception to prevent the application from crashing and provide the user with a more friendly error message, However, since I am doing async web calls, simply surrounding the web service calls by a try- catch does not help.
According to the exception details it happens in the End_FunctionName function that is defined inside the auto-generated proxy file.
Any tips about how to be able to gracefully handle this exception ?
Its pretty difficult to know exactly what is happening; however, I'm going to assume you have a web service like such
[ServiceContract]
public interface IMyService
{
[OperationContract]
String Hello(String Name);
[OperationContract]
Person GetPerson();
}
You probably have a proxy like this :
public class MyPipeClient : IMyService, IDisposable
{
ChannelFactory<IMyService> myServiceFactory;
public MyPipeClient()
{
//This is likely where your culprit will be.
myServiceFactory = new ChannelFactory<IMyService>(new NetNamedPipeBinding(), new EndpointAddress(Constants.myPipeService + #"/" + Constants.myPipeServiceName));
}
public String Hello(String Name)
{
//But this is where you will get the exception
return myServiceFactory.CreateChannel().Hello(Name);
}
public Person GetPerson()
{
return myServiceFactory.CreateChannel().GetPerson();
}
public void Dispose()
{
((IDisposable)myServiceFactory).Dispose();
}
}
If you have an error connecting you will get it not when you try to connect to the channel factory but when you actually try to call a function.
To fix this problem, you can put a try catch around every single function call and handle async calls manually.
Conversely, you can have a function like init() that is called synchronously every single time you instantiate a connection. This way you know that if that call connects that you have a connection.
If you are at risk of a connection dropping at any time I advise you go with the former option.
Anyway here is an example of how you'd fix it:
public class MyPipeClient : IMyService, IDisposable
{
ChannelFactory<IMyService> myServiceFactory;
public MyPipeClient()
{
myServiceFactory = new ChannelFactory<IMyService>(new NetNamedPipeBinding(), new EndpointAddress(Constants.myPipeService + #"/" + Constants.myPipeServiceName + 2) );
}
public String Hello(String Name)
{
try
{
return Channel.Hello(Name);
}
catch
{
return String.Empty;
}
}
public Person GetPerson()
{
try
{
return Channel.GetPerson();
}
catch
{
return null;
}
}
public Task<Person> GetPersonAsync()
{
return new Task<Person>(()=> GetPerson());
}
public Task<String> HelloAsync(String Name)
{
return new Task<String>(()=> Hello(Name));
}
public void Dispose()
{
myServiceFactory.Close();
}
public IMyService Channel
{
get
{
return myServiceFactory.CreateChannel();
}
}
}
I uploaded the source I wrote so that you could download the full source. You can get it here : https://github.com/Aelphaeis/MyWcfPipeExample
PS : This Repository throws the exception you are getting. In order to remove it just go to MyPipeClient and remove the + 2 in the constructor.
If you are using a Duplex, Consider using this repository:
https://github.com/Aelphaeis/MyWcfDuplexPipeExample

Categories

Resources