To invoke a method asynchronously by using BeginInvoke and save the result in Session state
Asked Answered
G

2

1

I have this sample code that calls a method asynchronously using begininvoke, I'm executing this on a button click event on a webform.

After the button click, the user is redirected to a different page, where the user waits for the result.

The AuthorizePayment method takes a long time to run and returns a int code. I want to store that int value somewhere in session or a cookie(but not to dispaly) When I access Session to add that code, it throws null exception. How do I save this result in a session or a cookie?

Any idea?

    public class CreditCardAuthorizationManager
{
  // Delegate, defines signature of method(s) you want to execute asynchronously
  public delegate int AuthorizeDelegate(string creditcardNumber, 
                                        DateTime expiryDate, 
                                        double amount);

  // Method to initiate the asynchronous operation
  public void StartAuthorize()
  {
      AuthorizeDelegate ad = new AuthorizeDelegate(AuthorizePayment);
      IAsyncResult ar = ad.BeginInvoke(creditcardNumber, 
                                       expiryDate, 
                                       amount,                  
                                       new AsyncCallback(AuthorizationComplete), 
                                       null);
  }

  // Method to perform a time-consuming operation (this method executes 
  // asynchronously on a thread from the thread pool)
  private int AuthorizePayment(string creditcardNumber, 
                               DateTime expiryDate, 
                               double amount)
  {
    int authorizationCode = 0;

    // Open connection to Credit Card Authorization Service ...
    // Authorize Credit Card (assigning the result to authorizationCode) ...
    // Close connection to Credit Card Authorization Service ...
    return authorizationCode;
  }

  // Method to handle completion of the asynchronous operation
  public void AuthorizationComplete(IAsyncResult ar)
  {
    // See "Managing Asynchronous Completion with the EndInvoke Method"
    // later in this chapter.
  }
}
Gang answered 14/7, 2012 at 22:18 Comment(4)
Much more information is needed to know why you can't access the session. Are these aspx pages? How does the target page "wait" for the response, are you polling something from script? I agree with @usr generally that you should be using ajax instead of starting threads from an aspx codebehind (if that's what you're doing) but there's a lot of missing info here about the architecture to know what could be wrong.Resnatron
@jamietre This is the real scenario.The real scenario is getting a token from a third party vendor at the time of logging in to my application. I don't want my user to wait for the third party vendor to return the token( in case vendor system is slow). This token is something good to have, but not mandatory. If we have the token, it will be used all over the application not in one page.Gang
@jamietre I was able to access the session only if I pass the HttpContext.Current to the delegate. I'm only worried because this delegate can outlive the lifetime of the page request, and also the valid lifetime of the HttpContext object assigned to that page request.Gang
I think I understand - are you saying that you are passing the HttpContext to your background thread and it's no longer available (e.g. null) when you later try to access it from that thread? Never tried that but makes sense. Use another mechanism to share this information between your background thread and the active session, e.g. try passing the Cache property of the current HttpContext - the cache is global and not bound to a specific context so I would think a reference to that should not die with the context. If that doesn't work then go right to the database?Resnatron
C
0

You cannot reliably execute work across HTTP requests because the ASP.NET worker process might be aborted at an arbitrary moment. The payment might have been executed by the provider but your website might think it hasn't (and the payment is lost). I certainly wouldn't want to enter my credit card data into such a system.

A better way to do this is to run the payment on an AJAX request and make the authorization code execute synchronously.

Crapulous answered 14/7, 2012 at 22:36 Comment(10)
Well, this is just an example. My real scenario is not a credit card system. So, from what you are saying is, there is now way I can save that authorizationCode(or call it some other name) temporarily on the user's session?Gang
The real scenario is getting a token from a third party vendor at the time of logging in to my application. I don't want my user to wait for the third party vendor to return the token( in case vendor system is slow). This token is something good to have, but not mandatory. If we have the token, it will be used all over the application not in one page. Does that make sense?Gang
Ok, that makes it a lot more workable. Although the problem with aborting workers remains: The async network IO could be aborted. However there is a way to keep the worker process alive (research for RegisterObject). Btw, from my point of view it makes sense what you are doing. Session: Yes you can store it there. My issue was with the time span in which the network IO is still running (and might be killed randomly).Crapulous
Understand. Keeping the getting killed randomly part aside. How do I store it in session? I'm not able to access session, it throws error in runtime. How do I get hold of session to add this vendor key in AuthorizationPayment method?Gang
@BrokenLink, that's another question. "How to access the session from a threadpool thread"Crapulous
"The payment might have been executed by the provider but your website might think it hasn't" This could just as easily happen whether or not it's AJAX, whether or not it's synchronous. There's never any guarantee of a response from the server. A good design should assume this could happen, I mean, someone could unplug their computer after they hit "send" for all you know.. if it doesn't get the response it expects, then it should be able to just ask the server again what happened. I agree that an ajax model is better but it doesn't remove any possible communication failures.Resnatron
.. also synchronous ajax requests are almost always bad, because they cause the browser to freeze (sometimes including GIF animations which can make users think something is wrong). I can't think of any reason not to use async. They offer no special reliability over async and a worse user experience.Resnatron
@jamietre, the AJAX request is there to keep the worker process alive. If the webservice call happens in the context of an http request the worker will not be aborted because running request are not aborted. (Instead the worker process is drained first.) The user-visible website would display a progress animation and say "please wait while your payment is being processed". That way the user sees a responsive website and latency is not an issue anymore (which was the reason the OP used async calls).Crapulous
I don't see why he would need a background thread at all if he was using a regular AJAX request. Perhaps we are confusing terminology, I thought you were recommending a synchronous ajax request, but you now seem to be describing a regular async request. I still don't totally understand his architecture. But given his description of the remote server response as "optional" perhaps he plans to only have the client wait for a certain amount of time before just proceeding anyway, but wants the server-initiated thread to continue regardless, in which case I guess his approach makes some sense.Resnatron
I propose a regular async AJAX call and synchronous server-side processing for that request. Easy, predictable and reliable. The client can abort the request after a timeout if that is what'S desired. This will not abort the servers progress (just its response will be thrown away).Crapulous
T
0

Instead of creating begin invoke and using delegates you can create a method with async keyword and returning the Task.

In your original method you can call the async method as

 Task<int> authorizationValue = await yourAsyncMethodCall(creditcardNumber, expirydate, amount)

  session["authorisation"] = authorizationValue;

Let me know if you need more specific example (my VS.net is broken so i had to type in here :()

Tyra answered 1/9, 2016 at 13:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.