Call synchronous webservice methods asynchronously - c#

I have to call a webservice method about a hundred times in a loop with different parameters everytime.
The webservice has only sync methods. Currently I am testing this in an console application and it takes over ten minutes to get the data when doing it synchronously!
What i want:
Run 10 requests in parallel. When they have finished, execute the next ten calls.
This should of course be async.
The functionality will be hosted in an IIS hosted wcf service.
Overview:
Client calls wcf service with params once. The wcf service method should call another webservice a hundred times asynchronously and save the final data to Excel.
I read, that Task.Run isn't a good idea when used in web application.
So, how to call sync web service methods asynchronously in a web context?
I am using the CRM Microsoft.Xrm.Sdk, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
var orders = _serviceProxy.RetrieveMultiple( new FetchExpression(strXmlFetch));
RetrieveMultiple gets an xml fragment (strXmlFetch) where the query is defined and executes the request.
The method resides here:
Namespace Microsoft.Xrm.Sdk.Client
Class OrganizationServiceProxy
public EntityCollection RetrieveMultiple(QueryBase query);
Under the hood the client SDk does the follwoing when calling RetrieveMultiple.
using (OrganizationServiceContextInitializer organizationServiceContextInitializer = new OrganizationServiceContextInitializer(this))
{
entityCollection = base.ServiceChannel.Channel.RetrieveMultiple(query);
return entityCollection;
}
I didn't implement anything yet, as i need a starting point for the async execution for the requests.

This should of course be async.
Well, that would be ideal. However, OrganizationServiceProxy does not have asynchronous methods (neither does IOrganizationService, so using the ServiceChannel.Channel directly won't help, either).
So, the first thing to do is ask Microsoft to add asynchronous APIs to that client library.
I read, that Task.Run isn't a good idea when used in web application.
Normally that's true; you'd want to call asynchronous APIs. Which in this case aren't available.
So, how to call sync web service methods asynchronously in a web context?
This isn't possible. Your options are:
Just keep it synchronous, one at a time. Yeah, it'll take longer than it should.
Make it parallel.
The problem with parallel processing on the server side is that you're now using N threads for a single request, which can really quickly bring your web server to its knees. I do not recommend this approach in production code, especially if it's exposed over the Internet.
However, if you're sure that there won't be too many calls to your WCF service, you can implement it in parallel.
Since you need to collect results from the parallel query, I'd recommend using Parallel LINQ (AsParallel) specifying WithDegreeOfParallelism(10), something like:
IEnumerable<EntityCollection> GetAll(OrganizationServiceProxy proxy, IEnumerable<QueryBase> queries)
{
return queries.AsParallel().WithDegreeOfParallelism(10)
.Select(query => proxy.RetrieveMultiple(query));
}

The webservice has only sync methods
Web services by default produce Asynchronous methods pertaining to BeginXX, EndXX, which are part of Asynchronous Programming Model, but they are not the one which can be consumed by Async-Await, since they don't return the Task.
What i want: Run 10 requests in parallel. When they have finished, execute the next ten calls. This should of course be async.
In truly Async calls as no threads are invoked and as it works on IO completion ports, therefore you can start many more calls, than just 10. Otherwise the logic to schedule 10 Async requests at a time has to be custom, there's no out of box mechanism to do it, even while using Threadpool, there's no guarantee of number of requests in parallel, though you may set the Max Degree of Parallelism, for higher limit for Parallel APIs.
Overview: Client calls wcf service with params once. The wcf service method should call another webservice a hundred times asynchronously and save the final data to Excel.
You would prefer all the calls to be Async, so that the Ui thread is not blocked, whether its to the wcf service or web service.
I read that Task.Run isn't a good idea when used in web application.
True, since a ThreadPool thread is getting invoked, which will do nothing post dispatching the IO call to Web Service
So, how to call sync web service methods asynchronously in a web context?
Needs an Async wrapper over Sync method, which is Anti-Pattern, but there's no other option. This would need a ThreadPool thread, something like:
public class AsyncWrapper
{
public async Task CallWcfAsync(<Parameters>)
{
SynchronizationContext _synchronizationContext =
SynchronizationContext.Current;
try
{
SynchronizationContext.SetSynchronizationContext(null);
await Task.Run(() => CallWcfMethod(<Parameters>));
}
finally
{
SynchronizationContext.SetSynchronizationContext
(_synchronizationContext);
}
}
}
Important Points:
CallWcfAsync is the Async wrapper method which you need.
Notice I have set the Synchronization context to Null before execution and then reset it, this is similar in behavior to ConfigureAwait(false), else in the web applications it would lead to deadlock, as Sychronization Context waited upon is blocked.

Related

Should I use Task.Run or Task.FromResult?

I have a controller which calls a service method, this method calls synchronous a stored procedure
Service:
public class ServiceSomething : IServiceSomething, IService {
public int ProcessSomethingInDatabase(List<SqlParameter> sqlParameters){
IConnection connection = ConnectionFactory.GetConnection();
//This makes a synchronous call
DataTable dataTable = Connection.DalDataTable("sp_process_something", sqlParameters);
int result = GetResultFromDataTable(dataTable);
return result;
}
}
Controller:
public class SomethingController : Controller {
private readonly IServiceSomething _serviceSomething;
[HttpPost]
public async Task<CustomResult> ProcessSomethingInDatabase(Criteria criteria){
List<SqlParameter> sqlParameters = CriteriaToSqlParams(criteria);
int result = await Task.Run(() => _serviceSomething.ProcessSomethingInDatabase(sqlParameters));
return new CustomResult(result);
}
}
That process could take a long time (from 30 seconds to 1 hour on some occasions).
The problem is that the synchronous call freezes the application, and because of that we use Task.Run. It has been requested to me that the thread don't be initialized in the controller.
Now I would like to know what is the best implementation, so much as not to freeze the application and to handle a process that can take hours to finish.
Should I really create a thread for this?
public async Task<int> ProcessSomethingInDatabaseAsync(List<SqlParameter> sqlParameters){
IConnection connection = ConnectionFactory.GetConnection();
return await Task.Run(() => {
DataTable dataTable = Connection.DalDataTable("sp_process_something", sqlParameters);
int result = GetResultFromDataTable(dataTable);
return result;
});
}
And the controller be
[HttpPost]
public async Task<CustomResult> ProcessSomethingInDatabase(Criteria criteria){
List<SqlParameter> sqlParameters = CriteriaToSqlParams(criteria);
int result = await _serviceSomething.ProcessSomethingInDatabaseAsync(sqlParameters);
return new CustomResult(result);
}
or should I use Task.FromResult?
public Task<int> ProcessSomethingInDatabaseAsync(List<SqlParameter> sqlParameters){
IConnection connection = ConnectionFactory.GetConnection();
DataTable dataTable = Connection.DalDataTable("sp_process_something", sqlParameters);
int result = GetResultFromDataTable(dataTable);
return Task.FromResult(result);
}
Note:
The service is hosted on a Windows Service and it is communicated through WCF
The short answer to your question is none of them, let's see why.
There are at least a couple of issues in the way you are trying to design your solution.
First of all you claimed that the operation you are trying to implement could take up until 1 hour of processing. This means that you must not execute that operation in the context of an HTTP request. HTTP requests are meant to be quick, any operation that can take a time greater than a few seconds should not be implemented via HTTP. Web clients, web servers and web infrastructure are all designed for quick processing of HTTP requests and there are timeouts everywhere which won't allow you to perform your operation inside of an HTTP request.
You can use an HTTP request to ask your backend to perform a long running operation. Your web stack will process the request and will decide whether or not the task that you are requesting can be started (based on your business rules), but that's it: the actual execution of the task must be delegated to backend services (for instance by using a queue).
This is a large topic, but I hope you get the idea: you can't use an action method to perform a long running operation; your action method should only validate the request to execute the operation and delegate the actual execution to someone else. You can read this blog post to get more information about this approach.
The second point which needs attention is the usage of Task.Run. The only valid reason to use Task.Runis calling CPU-bound workload from a UI thread (for instance calling a long running CPU-bound workload from an event handler of a windows form application). That's it. If you are in a scenario other than this one you should avoid the usage of Task.Run. If you have an operation which is asynchronous in nature, such as a database query, you should use an asynchronous api to execute that operation and await the result inside of an async method. In modern .NET code there is full support for asynchronous apis, so you can always use them to perform IO operations. If you are using a data access library which doesn't offer asynchronous apis, then change your data access library.
The usage of Task.Runis particularly dangerous in the context of ASP.NET applications. In ASP.NET applications you have a pool of threads which are used by the runtime to handle incoming HTTP requests. Each time you call Task.Run you are borrowing one of this threads to execute a workload (represented by the delegate that you pass to Task.Run). You are not expected to do that, because threads are an important resource in a web server and they should be used to serve incoming HTTP requests and handled by the ASP.NET runtime. By borrowing one threads for your Task.Run execution you are interfering with the ASP.NET thread pool management and you should not do that.
To summarize, if you are writing ASP.NET code:
use asynchronous apis each time you need to perform a workload which is truly asynchronous, such as a database query or an HTTP request to a web service. await the result of the asynchronous operation inside of an async method and never block on asynchronous operations using apis such as Task.Wait() and Task.Result
if you need to perform a CPU-bound synchronous workload inside of an action method, simply do that and do not fake asynchrony by wrapping your method call by using Task.Run. Generally speaking never use Task.Run in ASP.NET code: the ony place where it makes sense is UI client applications, such as windows forms or WPF applications. Use Task.Run each time you need to call long running cpu-bound workload from a UI thread, so that you do not freeze the application UI.
never execute operations which can last more than a few seconds inside of HTTP requests. HTTP requests are meant to be processed quickly. Use a queue mechanism to delegate to backend services the execution of long running tasks.
Consider reading this blog post for more information about the usage of Task.Run.
A final note on Task.FromResult<T>, that you mentioned in your question. This method is meant to create a Task instance representing a successfully completed asynchronous operation, having a specified result. That's it. This is not a way to fake asynchrony (generally speaking you should avoid faking asynchrony), it's just a way to solve the problem of creating a Task instance for an already completed operation producing a certain result.

Async Task.Run in a self hosted web service - is it "wrong usage" as long as it works?

I am working on a project with the following details:
No IIS or other 3rd party webservers involved
.NET 4.5 Windows application, which acts as a "server"
The server starts multiple WCF webservices, all of them self-hosted
The webservices are accessible to multiple users
Here is a most simple example of one of the many webservice methods:
public async Task<int> Count()
{
int result = 0;
//There is nothing to await here, so I use Task.Run
await Task.Run(() =>
{
using (IDB ctx = new DB())
{
result = ctx.Customers.All.Count();
}
//Here could happen a lot more
//System.Threading.Thread.Sleep(10000);
}).ConfigureAwait(false);
return result;
}
As you can see, I am using Task.Run to access some data, because non of the repository interfaces offers async methods. I can not await anything. If I wanted to do "real async", I would have to rewrite the complete repository interface.
If I would not use Task.Run, the server would be blocking all other incoming requests.
My 2 questions are:
Is there anything wrong using Task.Run in this scenario?
Even if it is working and maybe not completely wrong, is there a better, more professional solution to call synchronous code in an async method?
The initial reason for this question is, that I read, that using Task.Run in an async method is "fake async". (I think Task.Run starts a new thread, while "real async" code does not)
I answered my own question, see answer below. I hope it can help others.
Yes it is fake async and less scalable as it starts a thread and blocks it, there is no giving it back until its finished.
However,
as Stephen Clearly alludes to in his Task.Run Etiquette and Proper Usage
I call such methods “fake-asynchronous methods” because they look
asynchronous but are really just faking it by doing synchronous work
on a background thread. In general, do not use Task.Run in the
implementation of the method; instead, use Task.Run to call the
method. There are two reasons for this guideline:
Consumers of your code assume that if a method has an asynchronous signature, then it will act truly asynchronously. Faking
asynchronicity by just doing synchronous work on a background thread
is surprising behavior.
If your code is ever used on ASP.NET, a fake-asynchronous method leads developers down the wrong path. The goal of async on the server
side is scalability, and fake-asynchronous methods are less scalable
than just using synchronous methods.
Also Stephen Toub (aka Mr. Parallel) Should I expose asynchronous wrappers for synchronous methods?
The idea of exposing “async over sync” wrappers is also a very
slippery slope, which taken to the extreme could result in every
single method being exposed in both synchronous and asynchronous
forms. Many of the folks that ask me about this practice are
considering exposing async wrappers for long-running CPU-bound
operations. The intention is a good one: help with responsiveness.
But as called out, responsiveness can easily be achieved by the
consumer of the API, and the consumer can actually do so at the right
level of chunkiness, rather than for each chatty individual operation.
Further, defining what operations could be long-running is
surprisingly difficult. The time complexity of many methods often
varies significantly.
However, you actually really dont fit into either of these categories. From your description you are hosting this WCF service. It will run your code asynchronously anyway if you have set the InstanceContextMode and ConcurrencyMode correctly. Your will additionally have the ability to run the TBA wrappers for your call form the client, assuming you generated your proxies with the appropriate settings.
If i understand you correctly, you could just let this method be entirely synchronous, and let WCF take care of the details and save resources
Update
An example: If I use Task.Run inside any webservice methode, I can
even call Thread.Sleep(10000) inside Task.Run and the server stays
responsive to any incoming traffic.
I think the following might help you the most
Sessions, Instancing, and Concurrency
A session is a correlation of all messages sent between two endpoints.
Instancing refers to controlling the lifetime of user-defined service
objects and their related InstanceContext objects. Concurrency is the
term given to the control of the number of threads executing in an
InstanceContext at the same time.
Its seems like your WCF service is setup for InstanceContextMode.PerSession, and ConcurrencyMode.Single. If your service is stateless you probably want to use InstanceContextMode.PerCall and only use async when you have something that truly can be awaited
First of all: Thank all of you for your hints. I needed them to dive deeper into the problem.
I have found the real solution to this problem and I think, I could add some value to the community by answering my own question in detail.
The solution can also be found in this great article: https://www.oreilly.com/library/view/learning-wcf/9780596101626/ch04s04.html
Here is a quick summary of the initial problem and the solution:
The goal
My goal is to host multiple self-hosted WCF services in a .NET 4.5 application
All self-hosted WCF services are accessible to multiple clients
All self-hosted WCF services MUST NOT block each other when multiple users are using them
The problem (and the false solution) (my initial question)
My problem was, that whenever one client used a webservice, it would block the other webservices until it returned to the client
It did not matter what kind of InstanceContextMode or ConcurrencyMode I used
My false solution was to use async and Task.Run ("Fake Async"). It worked, but it was not the real solution.
The solution (found in the article)
When self-hosting WCF webservices, you MUST make sure, that you always call ServiceHost.Open in a seperate thread, different from the UI thread
Whenever you open a ServiceHost in a Console, WinForms or WPF application, or a Windows Service, you have to be aware, at which time you call ServiceHost.Open and how you use the ServiceBehaviorAttribute.UseSynchronizationContext
The default value for ServiceBehaviorAttribute.UseSynchronizationContext is True. (this is bad and leads to blocking!)
If you just call ServiceHost.Open, without setting UseSynchronizationContext = false , all ServiceHosts will run in the UI thread and block this thread and each other.
Solution 1 (tested and it works - no more blocking)
Set ServiceBehaviorAttribute.UseSynchronizationContext = false
Solution 2 (tested and it works - no more blocking)
Do NOT touch ServiceBehaviorAttribute.UseSynchronizationContext, just let it be true
But create at least one or multiple threads in which you call ServiceHost.Open
Code:
private List<ServiceHost> _ServiceHosts = new List<ServiceHost>();
private List<Thread> _Threads = new List<Thread>();
foreach (ServiceHost host in _ServiceHosts)
{
_Threads.Add(new Thread(() => { host.Open(); }));
_Threads[_Threads.Count - 1].IsBackground = true;
_Threads[_Threads.Count - 1].Start();
}
Solution 3 (not tested, but mentioned in the article)
Do NOT touch ServiceBehaviorAttribute.UseSynchronizationContext, just let it be true
But make sure, that you call ServiceHost.Open BEFORE the UI thread is created
Then the ServiceHosts will use a different thread and not block the UI thread
I hope this can help others with the same problem.

Wrapping a non async-method (which does web calls) into async

I know you should only use async for stuff which is not "CPU-intensive", e.g. file writes, web calls etc. and therefore I also know it doesn't makes sense to wrap every method into Task.Run or something similar.
However what should I do when I know a method does a web call, but it doesn't offer an async interface. Is it in this case worth to wrap it?
Concrete example:
I'm using CSOM (Client SharePoint Object Model) in my WebApi application (server) and want to get a SharePoint list.
This is normally done like this:
[HttpGet]
[Route("foo/{webUrl}")]
public int GetNumberOfLists(string webUrl)
{
using (ClientContext context = new ClientContext(webUrl))
{
Web web = context.Web;
context.Load(web.Lists);
context.ExecuteQuery();
return web.Lists.Count;
}
}
And I thought about changing it to something like this:
[HttpGet]
[Route("foo/{webUrl}")]
public async Task<int> GetNumberOfLists(string webUrl)
{
using (ClientContext context = new ClientContext(webUrl))
{
Web web = context.Web;
context.Load(web.Lists);
await Task.Run(() => clientContext.ExecuteQuery());
return web.Lists.Count;
}
}
Does it make sense and does it help? As I understand it, I just create / need a new thread for executing the query ("overhead") but at least the request thread will be free / ready for another request (that would be good).
But is it worth it and should it be done like this?
If so:
Isn't it strange that Microsoft doesn't offer the "async" method out of the box or did they just not care about it?
edit:
updated to use Task.Run as suggested in comment.
However what should I do when I know a method does a web call, but it doesn't offer an async interface.
Unfortunately still somewhat common. As different libraries update their APIs, they will eventually catch up.
Is it in this case worth to wrap it?
Yes, if you're dealing with a UI thread. Otherwise, no.
Concrete example... in my WebApi application (server)
Then, no, you don't want to wrap in Task.Run. As noted in my article on async ASP.NET:
You can kick off some background work by awaiting Task.Run, but there’s no point in doing so. In fact, that will actually hurt your scalability by interfering with the ASP.NET thread pool heuristics... As a general rule, don’t queue work to the thread pool on ASP.NET.
Wrapping with Task.Run on ASP.NET:
Interferes with the ASP.NET thread pool heuristics twice (by taking a thread now and then releasing it later).
Adds overhead (code has to switch threads).
Does not free up a thread (the total number of threads used for this request is almost equal to just calling the synchronous version).
As I understand it, I just create / need a new thread for executing the query ("overhead") but at least the request thread will be free / ready for another request (that would be good).
Yes, but all you're doing is jumping threads, for no benefit. The thread used to block on the query result is one less thread ASP.NET has to use to handle requests, so freeing up one thread by consuming another isn't a good tradeoff.
Isn't it strange that Microsoft doesn't offer the "async" method out of the box or did they just not care about it?
Some of the "older" MS APIs just haven't gotten around to adding async versions yet. They certainly should, but developer time is a finite resource.
This is my personal view of your problem and for me the above way is not required. When we host your API in IIS, the server assigns one thread from thread pool it has in the server. The IIS also has a setting of maxConcurrentRequestsPerCPU maxConcurrentThreadsPerCPU. You can setup these values to serve the request instead of handling the request all by yourself.

Benefits from returning a "Task<int>" instead of an "int" in a WCF or WebAPI method (IIS)

Consider a time consuming synchronous method "Foo":
public int Foo(int id)
{
// Do some expensive calculation
return 42;
}
And a WCF Service hosted in IIS called "FooService" which calls "Foo":
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class FooService
{
public Task<int> GetFoo(int id)
{
return Task.Factory.StartNew(() => return Foo(id));
}
public int GetFoo2(int id)
{
return Foo(id);
}
}
Do I get any benefits if I start and return a Task for Foo? Like - do I reduce load to the I/O Thread?
Tasks in WCF provide a more convenient API than that of the APM pattern (BeginXX/EndXX). Why should you use asynchronous calls in WCF? Because, when done correctly, it would lead to a much better thread utilization, which in turn, allows your application to be more scalable.
On the client side, having a task-based service contract, makes it easier to call a service without blocking a thread, waiting for the call to return. Even if the operation is a synchronous, CPU-bound operation on the server side, the client side would benefit from having a task. Also, with tasks, it's very simple to make the call synchronous again by calling Task.Wait() or Task<T>.Result.
On the service side, a task-based operation can be useful in several scenarios:
You want to parallelize some CPU-bound actions
You're doing IO (e.g. calling another service, reading a file, etc.)
Any combination of the above
Every time a WCF operation is called, WCF grabs a thread from the thread-pool to handle the request. So there's no need to call StartNew, which queues an operation to the thread-pool (a completely redundant overhead):
public Task<int> GetFoo(int id)
{
return Task.Factory.StartNew(() => return Foo(id));
}
Instead, you can use FromResult, which creates a completed task object:
public Task<int> GetFoo(int id)
{
return Task.FromResult(new Foo(id));
}
Lastly, if none of the above use cases are relevant, and your client API needs to be synchronous, there's no point in using tasks.
In my opinion, no, you won't get any benefit from spinning off a Task thread to do the work as described in your post. The reason is this: each WCF web service defined using WSHttpBinding or BasicHTTPBinding (can't speak to the others) will create its own independent thread for each incoming request, so I don't see the advantage. So if 10 requests coming at 12:00:01:00, each will get it's own thread from WCF automatically.
Now, if within each singular request you want threading to speed up some repetitive, time-consuming tasks, then you'd obviously benefit from setting up and managing threads through Task, but that's about it.
Please define a time consuming synchronous method
Overhead of asynchronous operations is not negligible. You should only use task if you are doing an IO-bound operation (calling another web service/API, reading a file, reading a lot of data from database or running a slow query).
Using async programming on server-side differs from client-side. We are not concerned by UI thread but by scalability. Using async programming on server-side will allow you to run thousands of concurrents requests.
This is very important since IIS 7, as explained here
... in IIS 7.0 integrated mode, ASP.NET restricts the number of
concurrently executing requests. The difference only matters when the
requests are asynchronous (the request either has an asynchronous
handler or a module in the pipeline completes asynchronously).
Obviously if the reqeusts are synchronous, then the number of
concurrently executing requests is the same as the number of threads
concurrently executing requests, but if the requests are asynchronous
then these two numbers can be quite different as you could have far
more requests than threads.
Note : since .net 4.5, it's better to use Task.Run.

Optimizing an ASMX web service with Multiple Long-Running Operations

I'm writing an ASP.NET web service using C# that has a DoLookup() function. For each call to the DoLookup() function I need my code to execute two separate queries: one to another web service at a remote site and one to a local database. Both queries have to complete before I can compile the results and return them as the response to the DoLookup method. The problem I'm dealing with is that I want to make this as efficient as possible, both in terms of response time and resource usage on the web server. We are expecting up to several thousand queries per hour. Here's a rough C#-like overview of what I have so far:
public class SomeService : System.Web.Services.WebService
{
public SomeResponse DoLookup()
{
// Do the lookup at the remote web service and get the response
WebResponse wr = RemoteProvider.DoRemoteLookup();
// Do the lookup at the local database and get the response
DBResponse dbr = DoDatabaseLookup();
SomeResponse resp = new SomeResponse( wr, dbr);
return resp;
}
}
The above code does everything sequentially and works great but now I want to make it more scalable. I know that I can call the DoRemoteLookup() function asynchronously ( RemoteProvider has BeginRemoteLookup / EndRemoteLookup methods) and that I can also do the database lookup asynchronously using the BeginExecuteNonQuery / EndExecuteNonQuery methods.
My question (finally) is this: how do I fire both the remote web service lookup AND the database lookup simultaneously on separate threads and ensure that they have both completed before returning the response?
The reason I want to execute both requests on separate threads is that they both potentially have long response times (1 or 2 seconds) and I'd like to free up the resources of the web server to handle other requests while it is waiting for responses. One additional note - I do have the remote web service lookup running asynchronously currently, I just didn't want to make the sample above too confusing. What I'm struggling with is getting both the remote service lookup AND the database lookup started at the same time and figuring out when they have BOTH completed.
Thanks for any suggestions.
You can use a pair of AutoResetEvents, one for each thread. At the end of thread execution, you call AutoResetEvents.Set() to trigger the event.
After spawning the threads, you use WaitAll() with the two AutoResetEvents. This will cause the thread to block until both events are set.
The caveat to this approach is that you must ensure the Set() is guarantee to be called, otherwise you will block forever. Additionally ensure that with threads you exercise proper exception handling, or you will inadvertently cause more performance issues when unhanded exceptions cause your web application to restart.
MSDN Has sample code regarding AutoResetEvent usage.
See Asynchronous XML Web Service Methods, How to: Create Asynchronous Web Service Methods and How to: Chain Asynchronous Calls with a Web Service Method.
But note the first paragraph of those articles:
This topic is specific to a legacy technology. XML Web services and XML Web service clients should now be created using Windows Communication Foundation (WCF).
BTW, doing things the way these articles say is important because it frees up the ASP.NET worker thread while the long-running task runs. Otherwise, you might be blocking the worker thread, preventing it from servicing further requests, and impacting scalability.
Assuming you can have a callback function for both the web request and the database lookup then something along these lines may work
bool webLookupDone = false;
bool databaseLookupDone = false;
private void FinishedDBLookupCallBack()
{
databaseLookupDone = true;
if(webLookupDone)
{
FinishMethod();
}
}
private void FinishedWebLookupCallBack()
{
webLookupDone = true;
if(databaseLookupDone)
{
FinishMethod();
}
}
I guess I don't have enough rep to upvote nor to comment. So this is a comment on John Saunders answer and Alan's comment on it.
You definitely want to go with John's answer if you are concerned about scalability and resource consumption.
There are two considerations here: Speeding up an individual request, and making your system handle many concurrent requests efficiently. The former both Alan's and John's answer achieve by performing the external calls in parallel.
The latter, and it sounds like that was your main concern, is achieved by not having threads blocked anywhere, i.e. John's answer.
Don't spawn your own threads. Threads are expensive, and there are already plenty of threads in the IO Threadpool that will handle your external calls for you if you use the asynch methods provided by the .net framework.
Your service's webmethod needs to be asynch as well. Otherwise a worker thread will be blocked until your external calls are done (it's still 1-2 seconds even if they run in parallel). And you only have 12 threads per CPU handling incoming requests (if your machine.config is set according to recommendation.) I.e. you would at most be able to handle 12 concurrent requests (times the # of CPUs). On the other hand if your web method is asynch the Begin will return pretty much instantenously and the thread returned to the worker thread pool ready to handle another incoming request, while your external calls are being waited on by the IO completion port, where they will be handled by threads from the IO thread pool, once they return.

Categories