Can someone kindly show me how to call a property of a WCF service from within a callback without deadlock occurring?
I've tried adding [CallbackBehavior(ConcurrencyMode=ConcurrencyMode.Multiple)] to the class which implements the callback, but without success.
The service has the following attribute:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single, InstanceContextMode = InstanceContextMode.Single)]
public class SAPUploadService : ISAPUploadService
{
Thanks MM
Here's the code which calls the Callback method
foreach (var otherConnection in _users.Keys)
{
SAPUploadInstruction ins = AddMessageToInstruction(message);
ins.UserName = user.UserName;
Task.Factory.StartNew(() =>
{
otherConnection.ReceiveInstruction(ins);
});
and here's the callback implementation of the ReceiveInstruction
public void ReceiveInstruction(SAPUploadInstruction instruction)
{
// fire this objects call back....
if (OnReceiveInstruction != null) OnReceiveInstruction(this, instruction);
}
In the above, the event OnReceiveInstruction is attached to the UI. This is handled as follows:
public void ReceiveInstruction(object sender, SAPUploadInstruction instruction)
{
DispatchIfNecessary(() => {
ProcessInstruction(instruction);
});
}
The method above - ProcessInstruction - sets various controls according to the service properties/functions. It is this that is getting deadlocked i.e Label1.Content = myService.SomeProperty.
BTW, DispatchIfNecessary is implemented as :
public void DispatchIfNecessary(Action action)
{
if (!Dispatcher.CheckAccess())
Dispatcher.Invoke(action);
else
action.Invoke();
}
In DispatchIfNecessary use asynchronous version of Invoke, so your callback won't wait for finishing UI changes, that can't be done because UI thread is waiting for end of callback processing (hence we have deadlock):
Dispatcher.BeginInvoke(action);
Related
I´m developing a service, that receives a HTTPPost request.
That request should start a long running process - (seconds to minutes).
At the client side I don´t want to wait all the time for the response, so I build the workflow as following:
client sends the request
server starts the job to do and immediately returns a guid - the job id
when the job completes on the server, it raises an event to inform the client about the completion (technically I use SignalR but that doesn´t matter)
A code sample:
[ApiController]
[Route("api/[controller]")]
public class JobsController : ControllerBase
{
private readonly IJobService jobService;
public JobsController(IJobService jobService)
{
this.jobService = jobService ?? throw new ArgumentNullException(nameof(jobService));
jobService.JobCompleted += id =>
{
// inform the client somehow
};
}
[HttpPost]
[Route("newJob")]
public IActionResult CreateNewJob(NewJob newJob)
{
var jobId = jobService.CreateNewJob(newJob);
return Ok(jobId);
}
}
public class JobService : IJobService
{
private readonly IImportantService importantService;
public event Action<string> JobCompleted;
public JobService(IImportantService importantService)
=> this.importantService = importantService ?? throw new ArgumentNullException(nameof(importantService));
public string CreateNewJob(NewJob newJob)
{
var id = Guid.NewGuid().ToString("H");
Task.Run(async () =>
{
// do the long running operation
Thread.Sleep(30000);
await importantService.DoSomethingAsync(newJob); // here is the problem - this service is already disposed
JobCompleted?.Invoke(id);
});
return id;
}
}
So the problem is, that I´m using the importantService in a Task that is running long after the request returned to the user.
But ASP.NET Core DI container is (normally) correctly disposing all services created for the request immediately.
How can I tell ASP.Net to not dispose importantService as long as the background task is still running?
Or is it better to do a complete different way - thinking about hangfire.io or similar...
Thank you! :)
You need to implement your service as a singleton see , that means it will keep alive throughout the application life cycle.
I'm not sure why this service is refusing to stop.
I ran into this when trying to correct a TimeoutException thrown when starting the service. Using:
public void OnStart()
{
_startTask = Task.Run(DoWork, _cancelTokenSource.Token);
}
private void DoWork(){ [listen for things and operate on them] }
public void OnStop()
{
_cancelTokenSource.Cancel();
_startTask.Wait();
}
I understand that implementing a simple timer will solve this, but that's not the point of my question. Why does the use of Task.Run(() => action, _tokenSource.Token) resolve the TimeoutException but causes the service to not respond to control messages?
The issue Observed
After installing and starting the service (it's TopShelf BTW), I'm unable to stop the service by conventional methods.
First Attempt:
All Subsequent Attempts:
Edit: Still no joy
Here is my attempt after following the provided example.
public void Start()
{
var token = _cancelTokenSource.Token;
Task.Factory.StartNew(() => Setup(token), token);
}
public void Stop()
{
_cancelTokenSource.Cancel();
//TearDown(); <-- original implementation of stop
}
private void Setup(CancellationToken token)
{
_mailman.SendServiceStatusNotification(_buildMode, "Started");
... create some needed objects
token.Register(TearDown);
InitializeInboxWatcherProcess(...);
}
private void TearDown()
{
_inboxWatcher.Terminate();
_mailman.SendServiceStatusNotification(_buildMode, "Stopped");
}
private void InitializeInboxWatcherProcess(...)
{
// pre create and initiate stuff
_inboxWatcher = new LocalFileSystemWatcherWrapper(...);
_inboxWatcher.Initiate();
}
public class LocalFileSystemWatcherWrapper : IFileSystemWatcherWrapper
{
// do FileSystemWatcher setup and control stuff
}
This is most likely because you either don't have a cancellation method, or there are subprocesses inside of DoWork() that are still running when you call Cancel(). As #Damien_The_Unbeliever said cancellation is a cooperative task.
When you call _cancelTokenSource.Cancel() if you haven't registered a callback function all that happens is that a boolean value isCancellationRequested is set to true, the DoWork() method is then responsible for seeing this and stopping its execution on its own. There is a flaw here, though, as you can probably tell, that if you have a time consuming loop running in the DoWork() task when Cancel() is called, that loop will have to finish an iteration before it can check the value of isCancellationRequested which can lead to hanging.
The way around this is to insert cancellation callback functions INTO the DoWork() method, see here and then register them to the token, so that when you call the Cancel() method, ALL of the tasks running in the background are stopped without having to wait for them.
Hope this helps!
So I'm working on a client that consumes a web service. I used the WSDL and XSD files from the service to generate the proxy class, and all of the synchronous functions work fine. However, given their synchronous nature, making any of the calls causes the UI to stop responding until the call is finished. Classic reason for using async methods, right?
Problem is, I'm still in school for my degree and know little about asynchronous programming. I've tried to read up on it online (my employer even has a Books 24x7 subscription) but I'm having a hard time grasping how I should make the calls and how to handle the response. Here's what I have:
/// <remarks/>
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://localhost:8080/getRecords", RequestNamespace="http://www.<redacted>.com/ws/schemas", ResponseNamespace="http://www.<redacted>.com/ws/schemas", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlArrayAttribute("records", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)]
[return: System.Xml.Serialization.XmlArrayItemAttribute("list", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=false)]
public record[] getRecords([System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)] string username, [System.Xml.Serialization.XmlArrayAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)] [System.Xml.Serialization.XmlArrayItemAttribute("list", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="integer", IsNullable=false)] string[] ids) {
object[] results = this.Invoke("getRecords", new object[] {
username,
ids});
return ((record[])(results[0]));
}
/// <remarks/>
public void getRecordsAsync(string username, string[] ids) {
this.getRecordsAsync(username, ids, null);
}
/// <remarks/>
public void getRecordsAsync(string username, string[] ids, object userState) {
if ((this.getRecordsOperationCompleted == null)) {
this.getRecordsOperationCompleted = new System.Threading.SendOrPostCallback(this.OngetRecordsOperationCompleted);
}
this.InvokeAsync("getRecords", new object[] {
username,
ids}, this.getRecordsOperationCompleted, userState);
}
private void OngetRecordsOperationCompleted(object arg) {
if ((this.getRecordsCompleted != null)) {
System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg));
this.getRecordsCompleted(this, new getRecordsCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState));
}
}
There's also this:
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.0.30319.1")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
public partial class getRecordsCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs {
private object[] results;
internal getRecordsCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) :
base(exception, cancelled, userState) {
this.results = results;
}
/// <remarks/>
public record[] Result {
get {
this.RaiseExceptionIfNecessary();
return ((record[])(this.results[0]));
}
}
}
and this:
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.0.30319.1")]
public delegate void getRecordsCompletedEventHandler(object sender, getRecordsCompletedEventArgs e);
I chose this example because the synchronous call has a return type and the async does not--at least not in the function call itself. I understand that the getRecordsCompletedEventArgs class has the proper return type, and that that is how I will get the data back from the call. What I can't seem to figure out is how to actually do that.
Let's say that I replace my current call to getRecords with getRecordsAsync:
How do I set up the client to respond when the async call completes? I need to drop the XML into a file using a LINQ procedure I've already written, I need to log the operation's success or failure, and I need to notify the user that the operation completed.
How can I ensure that making the call actually happens asynchronously? I remember reading at one point that simply invoking an asynchronous SOAP method doesn't actually happen asynchronously with regard to the current thread unless you do something else first. Any tips?
Are there any other major considerations that I'm missing? (Such as: "If you forget to do this, it'll blow up your program!")
These are all questions that I haven't been able to find convincingly firm answers to so far. Thank you in advance for any help you all can offer.
You need to handle the getRecordsCompleted event on the proxy which was auto-generated for you, like so:
private void Button_Click(object sender, EventArgs e)
{
var proxy = new WebServiceProxy();
// Tell the proxy object that when the web service
// call completes we want it to invoke our custom
// handler which will process the result for us.
proxy.getRecordsCompleted += this.HandleGetRecordsCompleted;
// Make the async call. The UI thread will not wait for
// the web service call to complete. This method will
// return almost immediately while the web service
// call is happening in the background.
// Think of it as "scheduling" a web service
// call as opposed to actually waiting for it
// to finish before this method can progress.
proxy.getRecordsAsync("USERNAME", new[] { 1, 2, 3, 4 });
this.Button.Enabled = false;
}
/// <summary>
/// Handler for when the web service call returns.
/// </summary>
private void HandleGetRecordsCompleted(object sender, getRecordsCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show(e.Error.ToString());
}
else
{
record[] result = e.Result;
// Run your LINQ code on the result here.
}
this.Button.Enabled = true;
}
If you use an auto-generated method on the proxy which ends with Async, the call will be made asynchronously - and that's it. What it sounds to me that you need to prove is that the call is non-blocking (that is, the UI thread does not have to wait for it to complete), and that's a bit tricky as you can't really inject custom logic into the auto-generated code.
A synchronous call made from a UI thread will block the UI and your application will become unresponsive. If that's not happening and your UI still responds to button clicks, keyboard events etc while the web service is running, you can be sure that the call is non-blocking. Obviously this will be tricky to prove if your web service call returns quickly.
You're not showing any client code so it's hard to say if you're missing anything.
For point 1
I think you are missing something on the code you are showing. Maybe the definition of getRecordsCompleted? It may be of type event I suppose, so you can attach a handler of type getRecordsCompletedEventHandler to your event so you can do something with the result of your asynchronous call.
Let's say your client proxy class name is RecordCleint
RecordClient client = new RecordClient();
//attaching an event handler
client.getRecordsCompleted += onGetRecordsCompleted;
//calling the web service asynchronously
client.getRecordsAsync("username", [ids]);
//definition of onGetRecordsCompleted of type getRecordsCompletedEventHandler
private void onGetRecordsCompleted(object sender, getRecordsCompletedEventArgs e)
{
if(e.Error != null)
{
record[] data = e.Result;
//do something with your data
}
else
{
//handle error
}
}
[Edit]
For point 2
If you are generating your client proxy with svcutil (Visual Studio > add Service reference) you can trust in it :) or you can watch the involved Threads with the Visual Studio Thread window.
For point 3
You might have some Thread synchronization problems, for example if you update some UI components in another Thread than the UI thread where they belong to. So you may need to do some extra work (dispatch).
Windows Forms (BeginInvoke or Dispatcher)
WPF (Dispatcher)
In my Silverlight usercontrol I am listening to events from the application and calls a WCF service to do some action
void SelectedCustomerEvent(string customer)
{
//.......
_wcfserviceagent.GetCustomer(customer, callback);
}
void callback(ObservableCollection<CustomerType> customer)
{
//do some action
}
In certain scenarios the event gets fired more than once when doing certain actions. The trouble is the callback is not necessarily called in the order of calls to the WCF service.
Is there anyway to make sure the calls and callback are always called in order?
Ideally, I want the the execution in such a way that for an event it will call the service and callback, and any other calls come in between will get queued. Of course, I can't block the UI thread.
The only way to ensure the sequence of calls to the WCF service is to implement your own queue on the client.
For example:
Queue<string> _customersQueue = new Queue<string>();
bool _fetching;
void SelectedCustomerEvent(string customer)
{
_customersQueue.Enqueue(customer);
//.......
if (!_fetching)
{
DoFetchCustomer(_customersQueue.Dequeue());
}
}
void DoFetchCustomer(string customer)
{
_fetching = true;
_wcfserviceagent.GetCustomer(customer, callback);
}
void callback(ObservableCollection<CustomerType> customer)
{
_fetching = false;
//do some action
if (_customersQueue.Count > 0)
{
DoFetchCustomer(_customersQueue.Dequeue());
}
}
I am looking for a way to test BeginInvoke on an Action method, since the method runs on a background thread there is no way of knowing when it actually completes or calls callback method. I am looking for a way to keep my test wait until the callback gets called before making assertions.
In the following Presenter class, you can notice that I am invoking PopulateView on background thread which updates the view when data is fetched and I am trying assert the view Properties are correctly initialized.
I am using NUnit and Moq.
public class Presenter
{
private IView _view;
private IService _service;
public Presenter(IView view, IService service)
{
_view = view;
_service = service;
Action action = PopulateView;
action.BeginInvoke(PopulateViewCallback, action);
}
private void PopulateViewCallback(IAsyncResult ar)
{
try
{
Action target = (Action)ar.AsyncState;
target.EndInvoke(ar);
}
catch (Exception ex)
{
Logger.Instance.LogException("Failed to initialize view", ex);
}
}
private void PopulateView()
{
Thread.Sleep(2000); // Fetch data _service.DoSomeThing()
_view.Property1 = "xyz";
}
}
Abstract your code so that you can inject the behavior you want at testing time.
public class MethodInvoker
{
public virtual void InvokeMethod(Action method, Action callback)
{
method.BeginInvoke(callback, method);
}
}
This version is asynchronous. At testing time, you can simply make a blocking version:
public class TestInvoker
{
public IAsyncResult MockResult { get; set; }
public override void InvokeMethod(Action method, Action callback)
{
method();
callback(MockResult);
}
}
Then your code simply changes to this:
// Inject this dependency
Invoker.InvokeMethod(PopulateView, PopulateViewCallback);
At runtime, it's asynchronous. At testing time, it blocks the call.
BeginInvoke() returns an IAsyncResult which you can use to wait.
IAsynchResult ar = action.BeginInvoke(...);
ar.AsyncWaitHandle.WaitOne();
You don't need to check that methods are called instead test the end result - in this case that _view.Propert1 == "xyz".
Because it's an async call you might need to have a loop that Asserts periodically that the value has been set, also a timeout on the test or the check is a must otherwise your test would never fail (just stuck).
You might consider stubbing out the action (PopulateView) in order to skip the Sleep.