How do I execute an asynchronous method from within a controller method and return an HttpStatusCodeResult(200), without the async delegate prematurely terminating its execution?
I am working on an asp.net application and one of my action my home controller takes long time to run(10-30 sec). I want to return an HttpStatusCodeResult(200) and keep running my function. The functoin need not to return anything, but still it's not really a case of fire and forget since I return a response to the server right away with HttpStatusCodeResult.
I tried using delegates, but it seems once I return the status code from the action, the delegate stops executing. Another option is to create a windows service but that would be like using a bazooka to kill a fly. I get very few requests so resources and performance is not an issue in my case. I heard ThreadPool.QueueUserWorkItem is an option, but which is most suitable for my case?
Create a static class that manages this long-running operation. This class will be responsible for creating threads to perform the task and also provides a way to check up on the status of any ongoing operations (i.e. if it's still in progress or has finished, and if so, what the result is).
Your MVC Controller method then uses this class to do the work and present status information or the completed processed data to the user.
Something like this:
public ActionResult GetSomething(String id) {
TaskResult result = TaskClass.GetStatus( id );
if( result == null ) { // the task has not been run, so start it up
TaskClass.StartNew( id );
return new View("PleaseWait");
} else if( !result.IsFinished ) {
return new View("PleaseWait");
} else {
return View("Results", result);
}
}
public static class TaskClass {
private static Dictionary<String,TaskResult> _tasks;
public static TaskResult GetStatus(String id) {
// TODO: Make this code thread-safe
if( _tasks.ContainsKey(id) ) return _tasks[id];
return null;
}
public static void Start(String id) {
_tasks.Add( id, new TaskResult("Working") );
Thread thread = new Thread( SomeExpensiveOperation );
thread.Start( id );
}
}
At the end of SomeExpensiveOperation there would be code that marks the TaskResult as finished.
Related
I am using rxJava to fetch data from the database and show it in a recyclerview. The relevant code is shown below
function updateUI(){
ContactsLab contactsLab = ContactsLab.get(getActivity());
Subscription sub = contactsLab.getContactList().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.toList()
.subscribe(onContactsReceived());
mCompositeSubscription.add(sub);
}
ContactsLab is a singleton that returns an Observable of Contact objects.
onContactsReceived function is shown below
private Observer<List<Contact>> onContactsReceived(){
return new Observer<List<Contact>>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(List<Contact> contacts) {
if(mContactsAdapter == null) {
mContactsAdapter = new ContactsAdapter(contacts);
mRecyclerView.setAdapter(mContactsAdapter);
} else{
mContactsAdapter.setContactList(contacts);
mContactsAdapter.notifyDataSetChanged();
}
}
};
}
The updateUI function is called in my fragment onResume but the view is updated only the first time. If I come back to this fragment from any other fragment (having added more items to db), onResume is called, updateUI runs and onContactsReceived also runs but returns immediately without calling onNext or onComplete.
I think this has something to do with the way rxJava handles observables but no idea how to fix it (read about defer but couldn't understand much). Can somebody please help?
Edit:
The getContactList function look like this :
public rx.Observable<Contact> getContactList() {
List<Contact> contacts = new ArrayList<>();
ContactCursorWrapper cursorWrapper = queryContacts(null, null);
try{
cursorWrapper.moveToFirst();
while (!cursorWrapper.isAfterLast()){
contacts.add(cursorWrapper.getContact());
cursorWrapper.moveToNext();
}
} finally {
cursorWrapper.close();
}
return rx.Observable.from(contacts);
}
Basically it queries the database and maps the returned Cursor into my Contact class(which is a POJO). I added the rx.Observable.from to get an observable that was later collated using toList and updated into the adapter.
I used this approach avoid having to call notifyDataSetChanged after getting each item (and call it only once after getting all that).
What's the right approach to minimize the number of notifyDataSetChanged calls and also, refresh each time onResume is called?
Your observable contactsLab.getContactList().toList() has terminated.toList() collects all emissions from a source observable to a list and emits the entire list once the source Observable terminates (see the documentation). You aren't going to observe any more emissions from it.
I'm just getting my feet wet with the SysOperation framework and I have some ReliableAsynchronous processes that run and call info("starting...") etc.
I want these infolog messages so that when I look in the BatchHistory, I can see them for purposes of investigating later.
But they also launch to the client, from the batch. And I can tell they're from the batch because you can't double click on the infologs to go to the source. Is there someway to either suppress these from popping up on the user's screen and only show in the batch log?
EDIT with some code:
User clicks a button on form action pane that calls an action menu item referencing a class.
In the class, the new method:
public void new()
{
super();
this.parmClassName(classStr(MyControllerClass));
this.parmMethodName(methodStr(MyControllerClass, pickTransLines));
this.parmExecutionMode(SysOperationExecutionMode::ReliableAsynchronous);
// This is meant to be running as a batch task, so don't load syslastvalue
this.parmLoadFromSysLastValue(false);
}
The main method hit from the menu item:
public static void main (Args _args)
{
MyControllerClass controller = new MyControllerClass();
MyContract contract;
WMSOrderTrans wmsOrderTrans;
RefRecId refRecId;
if (_args && _args.dataset() == tableNum(WMSOrderTrans) && _args.record())
{
contract = controller.getDataContractObject();
contract.parmRefRecId(_args.record().RecId);
controller.parmShowDialog(false);
refRecId = controller.doBatch().BatchJobId;
// This creates a batch tracking record
controller.updateCreateTracking(refRecId, _args.record().RecId);
}
}
The controller method that gets launched:
// Main picking method
private void pickTransLines(MyContract_contract)
{
MyTrackingTable tracking;
boolean finished;
BatchHeader batchHeader = BatchHeader::getCurrentBatchHeader();
boolean updateTracking = false;
// NOTE - This infolog launches after a few seconds to the user, but
// you can't double click on the info message to go to the code
// because it's fired from the batch somehow.
info(strFmt("Working on wmsordertrans.recid == %1", _contract.parmRefRecId()));
// Create/Update batch tracker if needed
if (this.isInBatch())
{
// NOTE - This code gets executed so we ARE in batch
this.updateTrackingStuff(...);
}
// Do the pick work
finished = this.doPick(_contract);
if(!finished)
throw error("An error occurred during the picking process.");
}
Then a split second later this launches to my session:
Look at the SysOperationServiceController.afterOperation method,:
[...]
if (_executionMode == SysOperationExecutionMode::ReliableAsynchronous)
{
batch = this.operationReturnValue();
if (batch)
{
infolog.view(Batch::showLog(batch.RecId));
}
}
[...]
This is the code that shows the infolog to the screen for reliable asynchronous processed.
You can create your own controller by extending SysOperationServiceController and use that on your menu item or in code, so do that and overwrite the afterOperation on your new controller, for example like this (didn't test but should work in your case):
if (_executionMode != SysOperationExecutionMode::ReliableAsynchronous)
{
super(_executionMode, _asyncResult);
}
SignalR does not have the ability to have client methods which returns a value. So I am trying to create a helper class to make this possible.
So this is what I am trying to do:
Server side: Call client method and provide unique request id Client(clientId).GetValue(requestId)
Server side: Save requestId and wait for answer using ManualResetEvent
Client side: Inside void GetValue(Guid requestId) call server method hubProxy.Invoke("GetValueFinished", requestId, 10)
Server side: find waiting method by requestId => set return value => set signal
Server side: Method not longer waiting vor ManualResetEvent and returns retrieved value.
I am able to get it work unfortunately. Here is my code:
public static class MethodHandler
{
private static ConcurrentDictionary<Guid, ReturnWaiter> runningMethodWaiters = new ConcurrentDictionary<Guid,ReturnWaiter>();
public static TResult GetValue<TResult>(Action<Guid> requestValue)
{
Guid key = Guid.NewGuid();
ReturnWaiter returnWaiter = new ReturnWaiter(key);
runningMethodWaiters.TryAdd(key, returnWaiter);
requestValue.Invoke(key);
returnWaiter.Signal.WaitOne();
return (TResult)returnWaiter.Value;
}
public static void GetValueResult(Guid key, object value)
{
ReturnWaiter waiter;
if (runningMethodWaiters.TryRemove(key, out waiter))
{
waiter.Value = value;
}
}
}
internal class ReturnWaiter
{
private ManualResetEvent _signal = new ManualResetEvent(false);
public ManualResetEvent Signal { get { return _signal; } }
public Guid Key {get; private set;}
public ReturnWaiter(Guid key)
{
Key = key;
}
private object _value;
public object Value
{
get { return _value; }
set
{
_value = value;
Signal.Set();
}
}
}
Using this MethodHandler class I need to have two method server side:
public int GetValue(string clientId)
{
return MethodHandler.GetValue<int>(key => Clients(clientId).Client.GetValue(key));
}
public void GetValueResult(Guid key, object value)
{
MethodHandler.GetValueResult(key, value);
}
Client side implementation is like this:
// Method registration
_hubProxy.On("GetValue", new Action<Guid>(GetValue));
public void GetValue(Guid requestId)
{
int result = 10;
_hubConnection.Invoke("GetValueResult", requestId, result);
}
PROBLEM:
if I call server side GetValue("clientid"). The client method will not be invoked. If I comment out returnWaiter.Signal.WaitOne();, client side GetValue is called and server side GetValueResult is called. But of course this time the method has already returned.
I thought is has to do with the ManualResetEvent but even using while(!returnWaiter.HasValue) Thread.Sleep(100); will not fix this issue.
Any ideas how to fix this issue?
Thanks in advance!
First, I think that, rather than asking for help in how to make it synchronous, it would be best if you just told us what it is you're trying to do so we could suggest a proper approach to do it.
You don't show your MethodHandler::Retrieve method, but I can guess pretty much what it looks like and it's not even the real problem. I have to tell you in the nicest possible way that this is a really bad idea. It will simply never scale. This would only work with a single SignalR server instance because you're relying on machine specific resources (e.g. kernel objects behind the ManualResetEvent) to provide the blocking. Maybe you don't need to scale beyond one server to meet your requirements, but this still a terrible waste of resources even on a single server.
You're actually on the right track with the client calling back with the requestId as a correlating identifier. Why can't you use that correlation to resume logical execution of whatever process you are in the middle of on the server side? That way no resources are held around while waiting for the message to be delivered to the client, processed and then the follow up message, GetValueResult in your sample, to be sent back a the server instance.
Problem solved:
The problem only occured in Hub.OnConnected and Hub.OnDisconnected. I don't have an exact explanation why, but probably these methods must be able to finish before it will handle your method call to the client.
So I changed code:
public override Task OnConnected()
{
// NOT WORKING
Debug.Print(MethodHandler.GetValue<int>(key => Clients(Context.ConnectionId).Client.GetValue(key)));
// WORKING
new Thread(() => Debug.Print(MethodHandler.GetValue<int>(key => Clients(Context.ConnectionId).Client.GetValue(key)))).Start();
return base.OnConnected();
}
I have created a TcmExtension named WorkflowEventSystem that has an event handler subscribed to the FinishProcess event. The purpose of this event is to schedule for publish all dependencies (i.e. pages) of the associated workflow subject.
The problem I am having is that even though the event triggers at the right time (a workflow process is completed), and all the items that are supposed to be scheduled for publish are, the PublishScheduler object created by the event never seems to go out of scope, and as such the WorkflowEventSystem object does not either.
Is there something I am missing about how the Event System works that would cause these objects to live on forever? I've included what I consider the relevant code below (some parts summarized). Thanks for any help.
Here's most of the actual TcmExtension:
public class WorkflowEventSystem : TcmExtension
{
public WorkflowEventSystem()
{
this.Subscribe();
}
public void Subscribe()
{
EventSystem.Subscribe<ProcessInstance, FinishProcessEventArgs>(ScheduleForPublish, EventPhases.All);
}
}
ScheduleForPublish creates a PublishScheduler object (class I created):
private void ScheduleForPublish(ProcessInstance process, FinishProcessEventArgs e, EventPhases phase)
{
if(phase == EventPhases.TransactionCommitted)
{
PublishScheduler publishScheduler = new PublishScheduler(process);
publishScheduler.ScheduleForPublish(process);
publishScheduler = null; // worth a try
}
}
The ScheduleForPublish method looks similar to this:
public void ScheduleForPublish(ProcessInstance process)
{
using(Session session = new Session("ImpersonationUser"))
{
var publishInstruction = new PublishInstruction(session);
// Set up some publish Instructions
var publicationTargets = new List<PublicationTarget>();
// Add a PublicationTarget here by tcm id
IList<VersionedItem> itemsToPublish = new List<VersionedItem>();
// Add the items we want to publish by calling GetUsingItems(filter)
// on the workflow process' subject
//Publish the items
PublishEngine.Publish(itemsToPublish.Cast<IdentifiableObject>(), publishInstruction, publishTargets);
}
}
Life-cycle management for TcmExtension classes is quite simple:
when you call Subscribe the TcmExtension object you specify is added to an internal list of subscriptions
when you later call Unsubscribe the same TcmExtension object is removed from the list of subscriptions
Since you never call Unsubscribe your WorkflowEventSystem is never removed and thus will never be garbage collected by .NET. And since your WorkflowEventSystem holds a reference to the PublishScheduler instance it created, that one will thus also never be cleaned up.
The proper boilerplate for a custom TcmExtension is:
public class WorkflowEventSystem : TcmExtension, IDisposable
{
EventSubscription _subscription;
public WorkflowEventSystem()
{
this.Subscribe();
}
public void Subscribe()
{
_subscription = EventSystem.Subscribe<ProcessInstance,
FinishProcessEventArgs>(ScheduleForPublish, EventPhases.All);
}
public void Dispose()
{
_subscription.Unsubscribe();
}
}
Nuno also gave a longer example (handling multiple subscribers) in this article:
http://nunolinhares.blogspot.nl/2012/07/validating-content-on-save-part-1-of.html
Web service calls are asynchronous in flex, but I wanted to wrap a web service call in a class to provide synchronous encapsulation. Something like the below - the user could call getMyMethodResult and get the result returned by the web service. I expected the thread that recieved the soap response would populate the variable _result and mean that getMyMethod would, after a time, find _result is not longer null. But it doesn't! Can anyone explain why this does not work?
public class myClass
{
private var _result:Object;
public function myClass()
{
//create a web service object
...
// Add listener
_service.addMyMethodListener(myMethodListener);
}
public function getMyMethodResult()
{
_service.myMethod();
while (_result == null)
{
// count a variable or something (unimportant)
}
return _result;
}
private function myMethodListener(event:Event):void
{
_result = event.result;
}
}
There's is absolutely no support for that. The event loop runs between frames and as long as you block the execution with your (infinite) loop, your myMethodListener function will not be called. Anyway, this would be a terrible idea since the absence of threading in the Flash Player will cause your UI to freeze while you wait for your service to return. You should just drop that idea.