views:

2057

answers:

8

Hi All, using a C# 3.5 Windows form, I am calling a stored procedure that can take 30 minutes to execute (please no knocks or debate on that...it’s doing a lot to a lot of data and is fully optimized). I don't want to make my user leave the app open for that entire time period, so I would like to call the sproc, let it fly and let them shut down the app and come back later. I am using out-of-the-box System.Data.SqlClient objects: SqlCommand, SqlConnection, etc.

Does anyone know if I can do this?

-Thx in advance!

Oh, against SQL Server 2005 if that makes any difference...

+1  A: 

let them shut down the app and come back later

If you're going to allow them to completely close the app, you'll have to start up a seperate .exe or something in a different ThreadPool that executes your code calling the stored procedure. Otherwise your thread will die when you close the app.

blesh
I agree, if you want your application to be able to close you'll need to completely move the sproc call out of the original process.
Joshua
+1  A: 

You can use the BeginExecuteXXX/EndExecuteXXX methods (depending whether it returns a result or not) of the SqlCommand, passing a callback delegate.

Mehrdad Afshari
I don't know why people down-vote without explaining why. This is a perfectly valid answer if they wanted to execute the procedure without fully shutting down the program.
Joshua
I can guess the reason: the ThreadPool thread used for BeginXXX will gets aborted when the main thread exists. However, I assumed the OP wants to keep the app open in the background rather than completely shutting down the process (which I think is a valid assumption.)
Mehrdad Afshari
I didn't mean to down-vote if I did that (new to this forum, so I may have clicked on accident and didn't realize what I was doing). I agree, perfectly good solution...but alas, I do want to let them completely kill the app. Thx Mehrdad.
Well, I guess it defintely wasn't me...I just tried to vote and I would have to log in to do so (and I'm not). Anyway, it was helpful.
You can't have any sort of *reliable* async execution based on BeginExecute for long running tasks. The client process has to stay around, maintaining an open connection, until the execution finishes. The async execution of SqlClient is geared toward scalable, nonblocking, execution of normal database access, not toward launching long running querries.
Remus Rusanu
I don't know what exactly you mean from "reliability" here. If I want to submit jobs to SQL server to reliably perform without expecting any results, I'd go with something like MSMQ or service broker but there are plenty of cases where you actually expect a result (e.g. generating a long running report) that you do want to perform in the background where Begin/End methods make perfect sense.
Mehrdad Afshari
+2  A: 

I suggest a re-architecture. Create a "work queue" table where you log requests to run the stored procedure. Then either have a Windows Service or a SQL Server job check that work queue from time to time (or be really ingenious and use a trigger) to kick off the stored procedure. Have the stored procedure update the progress from time to time in the work queue table, and your front-end can look at that an tell the user the progress, and then display the results when they're done.

Scott Whitlock
Well, I am not going to log in or register (sort of disappointed that I can't vote or close this out without doing that). Anyway, this was the best answer for me and then got backed up by a few supporting posts. All accept for the "...buy faster hardware" bit. I'd just assume claim failure, than tell my client they need to buy faster hardware, because I can't get it done right. Thanks for all your help!
@Dan - the "buy faster hardware" was just kidding. I'll edit it to remove it. But faster hardware is sometimes an option. You're not going to find hardware that's 30 times faster though! ;)
Scott Whitlock
@Scot, you are basically describing a built in feature: Service Broker Activation. Both Internal and External activation are arguably better than a windows service or an Agent job checking from time to time.
Remus Rusanu
@Remus - Agreed.
Scott Whitlock
+1  A: 

Another method that you could do would be to allow your application to run in the background (possibly in the notification area) and then exit or notify when the job completes. You could use this by using the BeginExecuteNonQuery and EndExecuteNonQuery methods to allow it to run in a separate thread.

Joshua
+2  A: 

If you really want to close down your application completely, I suggest you define a job in SQL Server Agent, and just execute a T-SQL statement to start that job manually. The syntax is:

sp_start_job 
     {   [@job_name =] 'job_name'
       | [@job_id =] job_id }
     [ , [@error_flag =] error_flag]
     [ , [@server_name =] 'server_name']
     [ , [@step_name =] 'step_name']
     [ , [@output_flag =] output_flag]

The job would execute your stored procedure. You will have to be a little creative to pass in any arguments. For example, insert the parameters into a "queue" table and have the job process all the rows in the queue.

Instead of a job, an insert trigger on your queue should work as well.

Thorarin
A: 

Your application's main window does not need to be open. If you launched it as a secondary thread, it will continue to run so long as IsBackground == false. I usually prefer to do this stuff through SQL Server Agent or as a client-server application (nothing prevents a client-server app from both running on the same machine, or even being the same binary).

It's been a while...

using System.Threading;

.....

Thread _t = null; void StartProcedure() { _t = new Thread(new ThreadStart(this.StartProc)); _t.IsBackground = false;//If I remember correctly, this is the default value. _t.Start(); }

bool ProcedureIsRunning { get { return _t.IsRunning; } //Maybe it's IsActive. Can't remember. }

void StartProc(object param) { //your logic here.. could also do this as an anonymous method. Broke it out to keep it simple. }

+1  A: 

I prefer to use a background service for offline processing, where your user app tells the service what to do and then disconnects. The service can log elapsed times and errors/status, and restart if necessary. WCF is designed for this and supports queues to communicate with.

ebpower
Remus had a good answer, and I appreciate him taking the time to post the blog article. That would be much less work than creating a separate background service if it's applicable to your problem.
ebpower
+8  A: 

This is actually a quite common scenario. You cannot do anything client based because the client may go away and disconnect and you'll loose the work achieved so far. The solution is to use Service Broker Activation: you create a service in the database and attach an activated procedure. In your application (or ASP page) you send a message to the service and embed the necessary parameters for your procedure. After your application commits, the message activates the service procedure. the service procedure reads the parameters from the message and invokes your procedure. since activation happens on a server thread unrelated to your original connection, this is reliable. In fact the server can even shutdown and restart while your procedure is being executed and the work will be rolled back then resumed, since the activating message will trigger again the service procedure after the restart.

Update

I have published the details of how to do this including sample code on my blog: Asynchronous procedure execution.

Remus Rusanu
Excellent details on blog!
Andrei Rinea