4

I need a console app which will calling webmethod.

It must be asynchronous and without timeout (we don't know how much time takes this method to deal with task.

Is it good way:

[WebMethod]
[SoapDocumentMethod(OneWay = true)]

??

John Saunders
  • 160,644
  • 26
  • 247
  • 397
user278618
  • 19,306
  • 42
  • 126
  • 196
  • 1
    Do you need a response from your webmethod? – lajuette Mar 21 '10 at 11:31
  • 5
    Using ASMX web services is not a "good way" unless you're stuck using .NET 2.0. Microsoft now considers ASMX web services to be "Legacy technology". All new web service development should be done using WCF. – John Saunders Mar 21 '10 at 17:00
  • 2
    An article in MSDN tagged with a note about ASMX being "legacy" (while newer articles in MSDN about ASMX do *not* carry that note) doesn't suddenly make ASMX worse at what it does. That whole debate is orthogonal to phenevo's question, at best. – Dave Ward Mar 21 '10 at 17:18
  • @Dave: that depends on whether the OP cares about how supported his solution will be. Some people like to be able to get bug fixes from Microsoft. Some don't care if bugs ever get fixed, I suppose. – John Saunders Mar 21 '10 at 17:55
  • 2
    ASMX is supported in .NET 4 and will continue to be for the foreseeable future beyond that (according to Microsoft; just ask them). If you have to resort to scare tactics to sell WCF, that speaks volumes about WCF. – Dave Ward Mar 22 '10 at 00:44
  • 1
    If you need a response, then you don't want to use OneWay since it precludes getting a response-- it's fire-and-forget. See my answer below for more details. – Justin Grant Mar 22 '10 at 21:24
  • @Dave Ward: Non-generic collections are also supported in new versions of .NET, but that doesn't mean you should use them unless you have a very good reason. ASMX Web Services only support the full WS-* spectrum through WSE, and WSE isn't even getting bug fixes (you cannot get the VS integration to work in VS 2008+ or on Vista/Win7 x64 at all). Not saying that we should all rush to change every ASMX service over to WCF right now, or that we should refuse to help with ASMX-related questions, but ASMX *is* a pretty bad idea for *new* projects. – Aaronaught Mar 24 '10 at 16:45

2 Answers2

5

Don't use one-way if you need results

First, if you need a response from your method, you don't want [SoapDocumentMethod(OneWay = true)]. This attribute creates a "fire and forget" call which never returns a response back to the caler and must return void. Instead, use a regular method call and call it async.

One method or two?

If you're using ASMX, there are two basic solutions: one method with a very long timeout, or two methods (as @Aaronaught suggested above): one to kick off the operation and return an ID of the operation, and another to pass in the ID and retrieve results (if available).

Personally, I would not recommend this two-method approach in most cases because of the additional complexity involved, including:

  • client and server code needs to be changed to suppport 2-step invocation
  • ASP.NET intrinsic objects like Request and Response are not available when called from a background task launched with ThreadPool.QueueUserWorkItem.
  • throttling on a busy server gets much harder if there are multiple threads involved with each request.
  • the server must hang onto the results until the client picks them up (or you decide to throw them out), which may eat up RAM if results are large.
  • you can't stream large, intermediate results back to the client

True, in some scenarios the 2-method approach may scale better and will be more resilient to broken network connections between client and server. If you need to pick up results hours later, this is something to consider. But your operations only take a few minutes and you can guarantee the client will stay connected, given the addiitonal dev complexity of the 2-method approach I'd consider it a last resort to be used only if the one-method solution doesn't match your needs.

Anyway, the solution requires two pieces. First, you need to call the method asynchronously from the client. Second, you need to lengthen timeouts on both client and server. I cover both below.

Calling ASMX Web Services Asynchronously

For calling an ASMX web service asynchronously from a command-line app, take a look at this article starting with page 2. It shows how to call a web service asynchronously from a .NET cilent app using the newer Event-Based Async Pattern. Note that the older .NET 1.0 approach described here, which relies on BeginXXX/EndXXX methods on the proxy, is not recommended anymore anymore since Visual Studio's proxy generator doesn't create those methods. Better to use the event-based pattern as linked above.

Here's an excerpt/adaptation from the article above, so you can get an idea of the code involved:

void KickOffAsyncWebServiceCall(object sender, EventArgs e)
{
    HelloService service = new HelloService();
    //Hookup async event handler
    service.HelloWorldCompleted += new 
        HelloWorldCompletedEventHandler(this.HelloWorldCompleted);
    service.HelloWorldAsync();
}

void HelloWorldCompleted(object sender,
                         HelloWorldCompletedEventArgs args)
{
    //Display the return value
    Console.WriteLine (args.Result);
}

Lengthen server and client timeouts

To prevent timeouts, http://www.dotnetmonster.com/Uwe/Forum.aspx/asp-net-web-services/5202/Web-Method-TimeOut has a good summary of how to adjust both client and server timeouts. You didn't specify in your question if you own the server-side method or just the client-side call, so the excerpt below covers both cases:

there has two setttings that will affect the webservice call timeout behavior:

** The ASP.NET webservice's server-side httpruntime timeout setting, this is configured through the following element:

httpRuntime Element (ASP.NET Settings Schema)
http://msdn2.microsoft.com/en-us/library/e1f13641.aspx

<configuration> <system.web>
<httpRuntime .............
executionTimeout="45"
.............../> </system.web> </configuration>

Also, make sure that you've set the <compilation debug="false" /> so as to make the timeout work correctly.

** If you're using the wsdl.exe or VS IDE "add webreference" generated proxy to call webservice methods, there is also a timeout setting on the client proxy class(derived from SoapHttpClientProtocol class). This is the "Timeout" property derived from "WebClientProtocol" class:

WebClientProtocol.Timeout Property http://msdn2.microsoft.com/en-us/library/system.web.services.protocols.webclientprotocol.timeout.aspx

Therefore, you can consider adjusting these two values according to your application's scenario. Here is a former thread also mentioned this:

http://groups.google.com/group/microsoft.public.dotnet.framework.webservices/browse_thread/thread/73548848d0544bc9/bbf6737586ca3901

Note that I'd strongly recommend making your timeouts long enough to encompass your longest operation (plus enough buffer to be safe should things get slower) but I wouldn't recommend turning off timeouts altogether. It's generally bad programming practice to allow unlimited timeouts since an errant client or server can permanently disable the other. Instead, just make timeouts very long--- and make sure to be logging instances where your clients or servers time out, so you can detect and diagnose the problem when it happens!

Finally, to echo the commenters above: for new code it's best to use WCF. But if you're stuck using ASMX web services, the above solution should work.

Community
  • 1
  • 1
Justin Grant
  • 44,807
  • 15
  • 124
  • 208
3

If the method is actually one-way, and you don't care about the result or ever need to follow up on the status of your request, then that is good enough.

If you do need a result (eventually), or need to check on the status of the operation, then this won't work very well. What your method should do in that case is start the work in a background thread, then immediately return an ID that can be used in a different web method to look up the status.

So something like this:

public enum JobStatus { Running, Completed, Failed };

public class MyService : WebService
{
    [WebMethod]
    public int BeginJob()
    {
        int id = GetJobID();
        // Save to a database or persistent data source
        SaveJobStatus(id, JobStatus.Running);
        ThreadPool.QueueUserWorkItem(s =>
        {
            // Do the work here
            SaveJobStatus(id, JobStatus.Completed);
        }
        return id;
    }

    [WebMethod]
    public JobStatus GetJobStatus(int id)
    {
        // Load the status from database or other persistent data source
        return ( ... )
    }
}

That's one method to start the work, and another method to check on its status. It's up to the client to poll periodically. It's not a very good system, but you don't have a lot of options with ASMX.

Of course, if you do need a response from this operation, a much better way is to use WCF instead. WCF gives you callback contracts, which you can use to begin a one-way operation and subscribe to a notification when that operation is complete, which eliminates the need for polling above.

So, to summarize all that:

  • If you don't need any response or status updates, just use IsOneWay = true.

  • If you do need updates, and can use WCF on the service side, use that with a callback contract. You should be using WCF for new Web Service projects anyway.

  • If you need updates and cannot use WCF, do the work in a background thread and implement a periodic polling system with an additional status-check web method.

Aaronaught
  • 120,909
  • 25
  • 266
  • 342