Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# .NET Web Service Asynchronously

I'm trying to implement an API that performs some really lengthy tasks using a Web Service. I basically want the Web Service to kick off a thread that performs the lengthy task and leaves it running until it completes. The issue is because its an API I want it to be cross-platform. So my questions are the following:

  • Is it possible to make asynchronous calls that do not require the client to be on .NET Framework? (It seems the Begin/End framework in place requires I return the .NET IASyncResult object) If so how can this be done? Totally unambiguous sample code would be amazingly helpful.
  • Because there is no state preservation in a web service, can this thread be later recovered? In the event that the client wants to cancel the process this would be highly important.
like image 331
jrbalsano Avatar asked Feb 24 '23 00:02

jrbalsano


2 Answers

Okay, back up a second.

If you want people to be able to call asynch, then they control and you just respond. If you want asynch work to be done on your side, then you need to institute a different pattern. One pattern is to set up a method that calls the work and then another method to see the status of the work. The first method returns some type of token/id that can be used to check the status on the other call. You then have a third method to pick up the results. This is all client driven.

You can, in theory, set up a callback mechanism, but the client needs to have a means of getting the answer, which is a service on their side. This is more complex, and less "public API" in nature, but it can work with clients.

The heterogenous nature of the system should not be a factor. And, you can institute both patterns, so clients that have a web service that can take the answer can fire and forget, while others poll.

The one downside of polling is you add more weight to your side, so set proper expectations and be prepared to throttle the guy that does something like this:

while(thread.NoComplete())
{
   pollTheCrapOutofService();
}

If you can get around heterogenous environs (open standards, et al), and can force .NET, you have other options, as Craig has mentioned.

like image 73
Gregory A Beamer Avatar answered Mar 02 '23 17:03

Gregory A Beamer


This is a good place to start:

http://msdn.microsoft.com/en-us/library/aa480516.aspx

If you implement this pattern, from the client perspective, nothing changes. You can implement a second web method to check the status of any running jobs you queue up in your async method.

From the sample code, modified somewhat to give you an idea of what you need to do (I don't expect this would compile):

[WebService]
public class AsyncWebService : System.Web.Services.WebService
{
public delegate string LengthyProcedureAsyncStub(
    int milliseconds, MyState state);

public string LengthyProcedure(int milliseconds, MyState state) 
{ 
    while(state.Abort == false)
    {
          //Do your work.  Check periodically for an abort
    }
    return state.Abort ? "Aborted" : "Success"; 
}

//This state object is what you can use to track invocations of your method
//You'll need to store it in a thread safe container.  Add it to the container in the Begin method and remove it in the end method.  While it's in the container other web methods can find it and use it to monitor or stop the executing job.
public class MyState 
{ 
    public Guid JobID = Guid.NewGuid();
    public object previousState; 
    public LengthyProcedureAsyncStub asyncStub; 
    public bool Abort = false;
}

[ System.Web.Services.WebMethod ]
public IAsyncResult BeginLengthyProcedure(int milliseconds, 
    AsyncCallback cb, object s)
{
    LengthyProcedureAsyncStub stub 
        = new LengthyProcedureAsyncStub(LengthyProcedure);
    MyState ms = new MyState();
    ms.previousState = s; 
    ms.asyncStub = stub;
    //Add to service wide container
    return stub.BeginInvoke(milliseconds, cb, ms);
}

[ System.Web.Services.WebMethod ]
public string EndLengthyProcedure(IAsyncResult call)
{
    //Remove from service wide container
    MyState ms = (MyState)call.AsyncState;
    return ms.asyncStub.EndInvoke(call);
}

[WebMethod]
public void StopJob(Guid jobID)
{
     //Look for the job in the service wide container
     MyState state = GetStateFromServiceWideContainer(jobID);
     state.Abort = true;
}
}

As far as the client is concerned, they are calling a webmethod called LenghtyProcedure that will not return until the job is complete.

like image 42
Joe Enzminger Avatar answered Mar 02 '23 18:03

Joe Enzminger