What happens in the case of a wcf session that has created an asynchronous function (such as background automated emailing ) when the client closes session ie client.Close()/client.Abort()?
In otherwords if have some as below:
[OperationContract(IsOneWay = true, IsInitiating = true, IsTerminating = false)]
void RegisterUser(string user);
[OperationContract(IsOneWay = false, IsInitiating = false, IsTerminating = false)]
void SendEmails(T[] listofDetails);
[OperationContract(IsOneWay = false, IsInitiating = false, IsTerminating = true)]
void UnRegisterUser();
and SendEmails creates a task or async function (the async call isn't done client side) that sends off all the emails in the background.
And the user Terminates the session before all the emails are finished sending? Does the session still stay open and wait or does it close and the emailing just keeps going until it finishes or what happens?
Thanks All
Sorry if the question is a bit ambiguous :/
In your example you have defined a request-response operation called SendEmails which returns void. This is not the same thing as an asynchronous operation.
I am assuming you are creating a new background thread within the operation method which then goes away and send the emails.
In this instance the operation will return void to the caller once the operation method has finished execution. If the client then terminates the channel the background thread will continue its work.
Related
I'm using MVC 4 and I have the following code :
public void DoWork(string connectionId)
{
connectionId = this.connectionId;
var a = MakeADelayAsync();
}
public async Task MakeADelayAsync()
{
await Task.Delay(5000);
var generalHubContext = GlobalHost.ConnectionManager.GetHubContext<GeneralHub>();
generalHubContext.Clients.Client(connectionId).showNotification("Completed");
}
"DoWork" method is my mvc action. what I intent to do is when the action button is pressed the "DoWork" calls an async method and returns to the client immediately. when the async method has completed his job it will notify client using signalR.
The problem is in the "MakeADelayAsync" method, those two lines after await won't be called ever. It seems that the flow never continues after await.
First question is Where is the problem in "MakeADelayAsync" ?
Second question is why do I have to write a useless code of var a = MakeADelayAsync(); to avoid compiler warning while I'm completely aware of what I am doing? I never use "a" anyway.
"DoWork" method is my mvc action. what I intent to do is when the action button is pressed the "DoWork" calls an async method and returns to the client immediately. when the async method has completed his job it will notify client using signalR.
Doing this is extremely dangerous. I strongly recommend that you use a persistent queue, as I said in my previous answer: Azure queue, MSMQ, WebSphere MQ, etc.
However, if you insist on doing it the dangerous way, then you can use the code that I have on my blog to execute background work on ASP.NET outside of a request context:
public void DoWork(string connectionId)
{
connectionId = this.connectionId;
// This is extremely dangerous code! If ASP.NET unloads
// your app, then MakeADelayAsync may not run to completion.
BackgroundTaskManager.Run(() => MakeADelayAsync());
}
First question is Where is the problem in "MakeADelayAsync" ?
You're executing code that is attempting to resume on the request context after the request is completed and the request context is disposed. It's the same problem you had before.
Second question is why do I have to write a useless code of var a = MakeADelayAsync(); to avoid compiler warning while I'm completely aware of what I am doing?
The compiler warning is telling you that the code is almost certainly a mistake... and the compiler is right.
can you try to mark your DoWork method as async?
public async void DoWork(string connectionId)
{
connectionId = this.connectionId;
var a = MakeADelayAsync();
}
I use WCF to async communicate between 2 processes.
till now I implemented the IAsyncResult pattern, and did it by having 3 methods:
BeginOperation - client.BeginOperation, when service receive it queue the job on threadpool with delegate for Operation
Operation - run on service side
EndOperation - this is what client callback.
My question is, I want to send strings from client to service, I want the send to be async, AND I dont want to get response - just want the service to print the string.
Is this enough ? This must be Non-Blocking
[OperationContract(IsOneWay = true)]
void PrintString(string message);
OR i need to do as following:
[OperationContract(IsOneWay = true, AsyncPattern=true)]
void BeginPrintString(string message, AsyncCallback callback, object state);
void EndPrintString(IAsyncResult asyncResult);
IsOneWay should be enough, but it still can block your client in specific circumstances, as well as throw errors.
See this and this posts for more details:
General things you should keep in mind about OneWay operations -
O/W operations must return void. O/W operations can still yield
exceptions. invoking an operation on the client channel might still
throw an exception if it couldn’t transmit the call over to the
service. O/W operations can still block. if the service is pumped with
messages and a queue had started, calling O/W operation may block your
following code.
You can make it more asynchronous by using a separate thread to do your work.
Instead of:
public void MyOneWayServiceOperation() {
// do stuff
}
do something like:
public void MyOneWayServiceOperation() {
new Thread(() => {
// do stuff
}).Start();
}
The service operation will have been executed on a new thread anyway, so it shouldn't cause any more problems for your code to be on a different new thread.
You can add a Thread.Sleep() call before your work if you want the service operation to have completed before you begin. This allowed me to quit the service from a service operation without having an exception thrown.
i am very new in WCF. so often gaze for wcf code & article. after viewing many code i often stuck for very basic things and got no elaborate discussion for the below question. so here are my few basic question....and looking for details discussion with sample situation and with sample code.
[OperationContract(IsInitiating=false, IsOneWay=false)]
bool Add_Contact(int sessionkey, string Newusrname);
What is the meaning of IsInitiating=false or true. when i should set it true or false ? what is the meaning of IsOneWay=false or true. when i should set it true or false ?
i belief my question are very basic and lots of scholar every time monitoring this forum. i hope i will get the best answer in details with many situation like when one should choose which option over other option with few sample code for simulating situations. thanks
IsInitiating
The IsInitiating parameter specifies whether or not an operation implemented by the associated method
can initiate a session on the server. Session instancing is the ability to have separate instances of a class
be maintained for each client channel. This property controls whether an operation is allowed to be the
first operation called when a session is created. The default for this parameter is true, meaning that the
specified operation can be the first called on a channel. In this scenario, all following calls to this method
have no effect (meaning, no other sessions are created). If this parameter is set to false, the client is forced
to call other methods prior to calling this method.
This comes in handy when you are trying to set an “order of operation,” meaning that you need a specific
method to be called first because the other methods called depend on something returned from the
first method.
For example, the following contains three methods, or service operations. The first operation creates the
session and must be the first method called. The final operation, Logout, closes the session:
[ServiceContract]
public interface IBuyStock
{
[OperationContract(IsInitiating = true, IsTerminating = false)]
void Login(user);
[OperationContract(IsInitiating = false, IsTerminating = false)]
void BuyStock(string stocksymbol, int quantity);
[OperationContract(IsInitiating = false, IsTerminating = true)]
void Logout(user);
}
Once the initiating method has been called, subsequent calls can be made to that method with no effect
to its initiating properties.
If any method other than the initiating method is called first, the following error is returned:
The operation ‘operationname’ cannot be the first operation to be called because
IsInitiating is false.
The initiating method must be called first, then other operations can be called.
IsOneWay
Service communication by default is bi-directional. Bi-directional service communication means that a
service operation can receive incoming messages and send a reply.
The IsOneWay parameter specifies whether a service operation returns a reply message. The default
value for this parameter is false, meaning that the method does not return a reply message.
The following example illustrates a one-way communication:
[ServiceContract]
public interface IBuyStock
{
[OperationContract(IsOneWay = true)]
void Login(user);
[OperationContract(IsOneWay = false)]
void BuyStock(string stocksymbol, int quantity);
}
In a one-way communication, the client initiates the communication and continues code execution and
does not wait for a response from the service. In a two-way communication, it waits for a response from
the service before continuing code execution.
The downside to using one-way communication is that the caller has no way of knowing whether or not
the service processed the message successfully.
Any methods that return a value where the IsOneWay property is set to false will return an exception.
IsTerminating
The IsTerminating property specifies whether a called service operation is to terminate the communication
session. The following example shows the last call,
Logout(), has the IsTerminating property set to true:
[ServiceContract]
public interface IBuyStock
{
[OperationContract(IsInitiating = true, IsTerminating = false)]
void Login(user);
[OperationContract(IsInitiating = false, IsTerminating = false)]
void BuyStock(string stocksymbol, int quantity);
[OperationContract(IsInitiating = false, IsTerminating = true)]
void Logout(user);
}
When the IsTerminating property is set to true, the session is closed after the reply message is sent (if
a reply message needs to be sent). On the client side, an IsTerminating value of true tells WCF to close
the channel only after the reply arrives at the client.
[OperationContract(IsInitiating = true, IsTerminating = false)]
Blockquote
In above code IsInitiating property is set to true when we want to create session .
Blockquote
We know login method is called first before other methods,hence we
use IsInitiating = true for login method and for other methods we set it as false.
Blockquote
In the same way we use Isterminating=True for logout method then session is deleted.
The question pretty much sums it up. I have a WCF service, and I want to wait until it finished to do something else, but it has to be until it finishes. My code looks something like this. Thanks!
private void RequestGeoCoordinateFromAddress(string address)
{
GeocodeRequest geocodeRequest = new GeocodeRequest();
GeocodeServiceClient geocodeService = new GeocodeServiceClient("BasicHttpBinding_IGeocodeService");
geocodeService.GeocodeCompleted += new EventHandler<GeocodeCompletedEventArgs>(geocodeService_GeocodeCompleted);
// Make the geocode request
geocodeService.GeocodeAsync(geocodeRequest);
//if (geocodeResponse.Results.Length > 0)
// results = String.Format("Latitude: {0}\nLongitude: {1}",
// geocodeResponse.Results[0].Locations[0].Latitude,
// geocodeResponse.Results[0].Locations[0].Longitude);
//else
// results = "No Results Found";
// wait for the request to finish here, so I can do something else
// DoSomethingElse();
}
private void geocodeService_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
{
bool isErrorNull = e.Error == null;
Exception error = e.Error;
try
{
double altitude = e.Result.Results[0].Locations[0].Latitude;
double longitude = e.Result.Results[0].Locations[0].Longitude;
SetMapLocation(new GeoCoordinate(altitude, longitude));
}
catch (Exception ex)
{
// TODO: Remove reason later
MessageBox.Show("Unable to find address. Reason: " + ex.Message);
}
}
There is a pattern, supported by WCF, for a call to have an asynchronous begin call, and a corresponding end call.
In this case, the asynchronous methods would be in the client's interface as so:
[ServiceContract]
interface GeocodeService
{
// Synchronous Operations
[OperationContract(AsyncPattern = false, Action="tempuri://Geocode", ReplyAction="GeocodeReply")]
GeocodeResults Geocode(GeocodeRequestType geocodeRequest);
// Asynchronous operations
[OperationContract(AsyncPattern = true, Action="tempuri://Geocode", ReplyAction="GeocodeReply")]
IAsyncResult BeginGeocode(GeocodeRequestType geocodeRequest, object asyncState);
GeocodeResults EndGeocode(IAsyncResult result);
}
If you generate the client interface using svcutil with the asynchronous calls option, you will get all of this automatically. You can also hand-create the client interface if you aren't using automatically generating the client proxies.
The End call would block until the call is complete.
IAsyncResult asyncResult = geocodeService.BeginGeocode(geocodeRequest, null);
//
// Do something else with your CPU cycles here, if you want to
//
var geocodeResponse = geocodeService.EndGeocode(asyncResult);
I don't know what you've done with your interface declarations to get the GeocodeAsync function, but if you can wrangle it back into this pattern your job would be easier.
You could use a ManualResetEvent:
private ManualResetEvent _wait = new ManualResetEvent(false);
private void RequestGeoCoordinateFromAddress(string address)
{
...
_wait = new ManualResetEvent(false);
geocodeService.GeocodeAsync(geocodeRequest);
// wait for maximum 2 minutes
_wait.WaitOne(TimeSpan.FromMinutes(2));
// at that point the web service returned
}
private void geocodeService_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
{
...
_wait.Set();
}
Obviously doing this makes absolutely no sense, so the question here is: why do you need to do this? Why using async call if you are going to block the main thread? Why not use a direct call instead?
Generally when using async web service calls you shouldn't block the main thread but do all the work of handling the results in the async callback. Depending of the type of application (WinForms, WPF) you shouldn't forget that GUI controls can only be updated on the main thread so if you intend to modify the GUI in the callback you should use the appropriate technique (InvokeRequired, ...).
Don't use this code with Silverlight:
private ManualResetEvent _wait = new ManualResetEvent(false);
private void RequestGeoCoordinateFromAddress(string address)
{
...
_wait = new ManualResetEvent(false);
geocodeService.GeocodeAsync(geocodeRequest);
// wait for maximum 2 minutes
_wait.WaitOne(TimeSpan.FromMinutes(2));
// at that point the web service returned
}
private void geocodeService_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
{
...
_wait.Set();
}
When we call _wait.WaitOne(TimeSpan.FromMinutes(2)), we are blocking the UI thread, which means the service call never takes place. In the background, the call to geocodeService.GeocodeAsync is actually placed in a message queue, and will only be actioned when the thread is not executing user code. If we block the thread, the service call never takes place.
Synchronous Web Service Calls with Silverlight: Dispelling the async-only myth
The Visual Studio 11 Beta inludes C# 5 with async-await.
See Async CTP - How can I use async/await to call a wcf service?
It makes it possible to write async clients in a 'synchronous style'.
I saw one guy did use ManualReset and waitAll, but he had to wrap all code inside of ThreadPool..
It is very bad idea...thought it works
I'm trying to implement a reconnect logic for a wcf client. I'm aware that you have to create a new channel after the current channel entered the faulted state. I did this in a channel faulted event handler:
internal class ServiceClient : DuplexClientBase, IServiceClient
{
public ServiceClient(ICallback callback, EndpointAddress serviceAddress)
: base(callback, MyUtility.GetServiceBinding("NetTcpBinding"), serviceAddress)
{
// Open the connection.
Open();
}
public void Register(string clientName)
{
// register to service
}
public void DoSomething()
{
// some code
}
}
public class ClientApp
{
private IServiceClient mServiceClient;
private ICallback mCallback;
public ClientApp()
{
mServiceClient = new ServiceClient( mCallback, new EndpointAddress("someAddress"));
mServiceClient.Register();
// register faulted event for the service client
((ICommunicationObject)mServiceClient).Faulted += new EventHandler(ServiceClient_Faulted);
}
void ServiceClient_Faulted(object sender, EventArgs e)
{
// Create new Service Client.
mServiceClient = new ServiceClient( mCallback, new EndpointAddress("someAddress"));
// Register the EI at Cell Controller
mServiceClient.Register();
}
public void DoSomething()
{
mServiceClient.DoSomething();
}
}
But in my unit test I still get a "The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state" exception.
Is it possible that the callback channel is still faulted and if yes how can I replace the callback channel?
so far I have experienced that a WCF connection needs to be recreated on fault - there doesn't seem to be a way to recover it otherwise. As for when a fault occurs, the method seems to fire fine, but often it fires and cleans up the WCF connection (establishing a new one, etc) as the current request is going through - causing this to fail - especially true on timeouts.
A couple of suggestions:
- If it is timeout related, keep track of the last time a call was made and a constant containing the timeout value. If the WCF connection will have been dropped due to inactivity, drop it and recreate it before you send the request over the wire.
- The other thing, it looks like you are not re-adding the fault handler, which means the first fault will get handled, but the second time it faults it will fall over without a handler cause no new one has been attached.
Hope this helps
Have you tried to reset the communications channel by calling mServiceClient.Abort in the Faulted event handler?
Edit:
I see that you do not reinitialize the mCallback object in your recovery code. You may need to assign it to a new instance.