Restoring database with SMO - Reporting progress problems - sql-server

I'm using the below to restore a database in VB.NET.
This works but causes the interface to lockup if the user clicks anything.
Also, I cannot get the progress label to update incrementally, it's blank until the backup is complete then displays 100%
Sub DoRestore()
Dim svr As Server = New Server("Server\SQL2008")
Dim res As Restore = New Restore()
res.Devices.AddDevice("C:\MyDB.bak", DeviceType.File)
res.Database = "MyDB"
res.RelocateFiles.Add(New RelocateFile("MyDB_Data", "C:\MyDB.mdf"))
res.RelocateFiles.Add(New RelocateFile("MyDB_Log", "C:\MyDB.ldf"))
res.PercentCompleteNotification = 1
AddHandler res.PercentComplete, AddressOf ProgressEventHandler
res.SqlRestore(svr)
End Sub
Is this change correct?:
Private Sub ProgressEventHandler(ByVal sender As Object, ByVal e As PercentCompleteEventArgs)
UpdateProgressBar(e.Percent)
End Sub
Private Sub UpdateProgressBar(ByVal e As String)
ProgressBar.Value = e
Status.Text = e.ToString
End Sub

You need to use SqlRestoreAsync not SqlRestore to prevent it tying up your main thread.
Then to avoid the Cross Thread Operation error when updating the UI you can use the approach here. Where you create a method on the form that will update the UI elements and call that from your asynch handler using Invoke.

Related

vb.Net: Creating UI in a BackgroundWorker

I'm working on an application to be able to monitor production information on a 3x3 (so 9 screen) video wall. One of the screen sets that I'm working on right now retrieves information and then formats for display on the screen. It takes about 2 seconds to retrieve and format this data (just a rough guess, not actually measured). Because it does 9 screen, one after the other, there is a very noticeable amount of time to switch to this screen set. The PC driving this video wall has 8 processing cores, so while one processor is chugging away doing all this work, there's plenty of processing power just sitting idle.
My first thought is that I need to use multi-threading. Unfortunately, I'm very new to this concept. I really have only used it one other time. I tried creating a BackgroundWorker and having the DoWork routine generate my UI. Unfortunately, it crashes the first time I try to create a UI element (Dim grLine as New Grid). I did manage to get around that by having a dummy DoWork routine and generating all my UI in the RunWorkerCompleted routine. This does allow my blank window to show up immediately, but none of the UI I'm generating shows up until it has all been rendered.
Here's a very cleaned up version of what I'm trying to do:
For i As Integer = 1 to 9
Dim win As New MyCustomWindow
win.DisplayScreen = i ' This function in MyCustomWindow sets the Bounds
win.MyShow({1, 2}) ' Sample args
Globals.VideoWall.Windows(i) = win
Next
The MyCustomWindow Class:
Class MyCustomWindow
Public Sub MyShow(a() as Integer)
Me.Show() ' Has a "Loading..." TextBlock
Dim bw as New ComponentModel.BackgroundWorker
AddHandler bw.DoWork, AddressOf Generate_UI_DoWork
AddHandler bw.RunWorkerCompleted, AddressOf Generate_UI_Complete
bw.RunWorkerAsync(a)
End Sub
Private Sub Generate_UI_DoWork((sender As Object, e As ComponentModel.DoWorkEventArgs)
' Pass our arguments to the Complete routine.
e.Result = e.Argument
End Sub
Private Sub Generate_OpsMarket_Complete(sender As Object, e As ComponentModel.RunWorkerCompletedEventArgs)
Dim IDs() as Integer
IDs = e.Result
Dim grLine As New Grid ' We crash here if this code is in DoWork instead of RunWorkerCompleted
For Each id As Integer In IDs
grLine.RowDefinitions.Add(New RowDefinition)
Dim txt as New TextBlock ' For a header
grLine.Children.Add(txt)
grLine.RowDefinitions.Add(New RowDefinition)
Dim MyCtrl as New MyCustomControl()
MyCustomControl.GetData(id)
grLine.Children.Add(MyCtrl.MyGrid)
txt.Text = MyCtrl.Header
Next
txLoading.Visibility = Visibility.Hidden
grRoot.Children.Add(grLine)
End Sub
End Class
I tried to leave enough detail in the code so hopefully it'll be evident what I'm trying to accomplish, but keeping it small enough to not be overwhelming.
Edited to add:
The bulk of the work happens in MyCustomControl.GetData(id) ... that Sub downloads the data from a web server (in JSON format), parses the JSON, then generates the rows (3) and columns (30 or 31, depending on the month) for the Grid and fills in the data it received from the web server.
Your current BackgroundWorker implementation give you no benefits, as you noticed by your self.
Your main problem is that your current code/logic tightly depend on UI controls. Which strict you with UI thread. Because creating/updating UI controls can be done only on UI thread - that's why you got Exception when trying create/update UI controls in BackgroundWorker.DoWork handler.
Suggest to separate logic which retrieve monitoring information in parallel and then you can create/update control on UI with already formatted data.
This is raw/pseudo example
Class DataService
Public Function GetData(ids As Integer()) As YourData
' Get data from web service
' Validate and Format it to YourData type or List(Of Yourdata)
Return data
End Function
End Class
Class MyCustomWindow
Public Sub MyShow(a() as Integer)
Me.Show() ' Has a "Loading..." TextBlock
Dim bw as New ComponentModel.BackgroundWorker
AddHandler bw.DoWork, AddressOf Generate_UI_DoWork
AddHandler bw.RunWorkerCompleted, AddressOf Generate_UI_Complete
bw.RunWorkerAsync(a)
End Sub
Private Sub Generate_UI_DoWork((sender As Object, e As ComponentModel.DoWorkEventArgs)
Dim service As New DataService()
Dim data = service.GetData(e.Argument)
e.Result = data
End Sub
Private Sub Generate_OpsMarket_Complete(sender As Object,
e As ComponentModel.RunWorkerCompletedEventArgs)
Dim data As Yourdata = DirectCast(e.Result, YourData)
'Update UI controls with already formatted data
End Sub
End Class
Update on Sub downloads the data from a web server
In this case you don't need multi-threading/parallel at all. Because you loading time is waiting time for response. In this case my advice will be using async/await approach, which will release UI thread(make it responsive) while you waiting for response from web-service.
Class DataService
Public Async Function GetDataAsync(ids As Integer()) As Task(Of YourData)
Using client As HttpClient = New HttpClient()
Dim response As HttpResponseMessage = Await client.GetAsync(yourUrl)
If response.IsSuccessStatusCode = True Then
Return Await response.Content.ReadAsAsync<YourData>()
End If
End Using
End Function
End Class
Then in the view you don't need BackgroundWorker
Class MyCustomWindow
Public Async Sub MyShow(a() as Integer) As Task
Me.Show() ' Has a "Loading..." TextBlock
Dim service As New DataService()
Dim data As YourData = Await service.GetDataAsync(a)
UpdateControlsWithData(data)
End Sub
Private Sub UpdateControlsWithData(data As YourData)
' Update controls with received data
End Sub
End Class
For what it's worth, here are a few examples which do 9 x 500ms of data operations, then simple UI operation.
The first example runs on a background thread but the main loop runs in sequence. The messagebox at the end shows that it takes around 4500 ms because of the 500 ms thread sleep is run sequentially. Notice how the DoWork method has nothing to do with the UI. It uses two threads: the UI thread and one background worker. Since it's not doing work on the UI, the form is responsive while the background worker is working.
Private bw_single As New BackgroundWorker()
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
AddHandler bw_single.DoWork, AddressOf bw_single_DoWork
AddHandler bw_single.RunWorkerCompleted, AddressOf bw_single_Complete
bw_single.RunWorkerAsync()
End Sub
Private Sub bw_single_DoWork(sender As Object, e As DoWorkEventArgs)
' runs on background thread
Dim data As New List(Of Integer)()
Dim sw As New Stopwatch
sw.Start()
For i As Integer = 1 To 9
' simulate downloading data, etc.
Threading.Thread.Sleep(500)
data.Add(i)
Next
sw.Stop()
e.Result = New Result(data, sw.ElapsedMilliseconds)
End Sub
Private Sub bw_single_Complete(sender As Object, e As RunWorkerCompletedEventArgs)
RemoveHandler bw_single.DoWork, AddressOf bw_single_DoWork
RemoveHandler bw_single.RunWorkerCompleted, AddressOf bw_single_Complete
' runs on UI thread
Dim res = CType(e.Result, Result)
Me.DataGridView1.DataSource = res.Data
MessageBox.Show(
String.Format("Performed on bw (single), took {0} ms, data: {1}",
res.Elapsed, String.Join(", ", res.Data)))
End Sub
(This is the class which holds the result of the background worker)
Private Class Result
Public Property Data As IEnumerable(Of Integer)
Public Property Elapsed As Long
Public Sub New(data As IEnumerable(Of Integer), elapsed As Long)
Me.Data = data
Me.Elapsed = elapsed
End Sub
End Class
The second example runs on a background thread but the main loop runs in parallel. The messagebox at the end shows that it takes around 1000 ms ... why? Because my machine like yours has 8 logical cores but we are sleeping 9 times. So at least one core is doing two sleeps and this will gate the entire operation. Again, there is one thread for the UI, one for the background worker, but for the parallel loop, the OS will allocate CPU time from the remaining cores to each additional thread. The UI is responsive and it takes a fraction of the time of the first example to do the same thing
Private bw_multi As New BackgroundWorker()
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
AddHandler bw_multi.DoWork, AddressOf bw_multi_DoWork
AddHandler bw_multi.RunWorkerCompleted, AddressOf bw_multi_Complete
bw_multi.RunWorkerAsync()
End Sub
Private Sub bw_multi_DoWork(sender As Object, e As DoWorkEventArgs)
' runs on background thread
Dim data As New ConcurrentBag(Of Integer)()
Dim sw As New Stopwatch
sw.Start()
Parallel.For(1, 9,
Sub(i)
data.Add(i)
Threading.Thread.Sleep(500)
End Sub)
sw.Stop()
e.Result = New Result(data, sw.ElapsedMilliseconds)
End Sub
Private Sub bw_multi_Complete(sender As Object, e As RunWorkerCompletedEventArgs)
RemoveHandler bw_multi.DoWork, AddressOf bw_multi_DoWork
RemoveHandler bw_multi.RunWorkerCompleted, AddressOf bw_multi_Complete
' runs on UI thread
Dim res = CType(e.Result, Result)
Me.DataGridView1.DataSource = res.Data
MessageBox.Show(
String.Format("Performed on bw (multi), took {0} ms, data: {1}",
res.Elapsed, String.Join(", ", res.Data)))
End Sub
Since the above two examples utilize background workers to do their work, they will not freeze the UI thread. The only code running on the UI is in the button click handlers and the RunWorkerCompleted handler.
Lastly, this example uses only a single UI thread. It will freeze the UI while it's running for 4500 seconds. Just so you know what to avoid...
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim data As New List(Of Integer)()
Dim sw As New Stopwatch
sw.Start()
For i As Integer = 1 To 9
' simulate downloading data, etc.
Threading.Thread.Sleep(500)
data.Add(i)
Next
sw.Stop()
Dim res = New Result(data, sw.ElapsedMilliseconds)
Me.DataGridView1.DataSource = res.Data
MessageBox.Show(
String.Format("Performed on bw (single), took {0} ms, data: {1}",
res.Elapsed, String.Join(", ", res.Data)))
End Sub
Summarily, you should figure out how to separate the data layer from the UI. See separation of concerns and the SO question, Why is good UI design so hard for some Developers?, and this one What UI design principles like “separation of concerns” can I use to convince developers that the UI needs fixing?

Updating Textbox based off SQL InfoMessages results

Branching off a previous question... Retrieving only PRINT command from SQL Server procedure in VB.NET
I was able to dump all the text from SQL Server's messages and use what I need but for some reason I am unable to update my textbox with the events.
Private Shared Sub OnInfoMessage(ByVal sender As Object, ByVal e As System.Data.SqlClient.SqlInfoMessageEventArgs)
Dim Counter As Integer
Counter = 1
For Each line In e.Message.Split(vbNewLine)
If (line.Contains("====")) Then
RestoreTool.txtTRNStatus.Text = "TRN #" & Counter & "Restored"
Using LogFile As IO.StreamWriter = New IO.StreamWriter("C:\SDBT\worklog.ft", True)
LogFile.WriteLine(line)
End Using
Counter += 1
End If
Next
Everything runs smoothly but the textbox (txtTRNStatus) does not update with anything and remains blank (Where it should be showing "TRN #1 Restored")
I am utilizing a Background Worker as well to call the procedure that actually performs the restoration of a database. That procedure is what contains the event handler for the SQLInfoMessages.
Dim SQL As SqlCommand
Dim DBConn As New SqlConnection(ConnectionString)
SQL = New SqlCommand(DBScript, DBConn)
AddHandler DBConn.InfoMessage, New SqlInfoMessageEventHandler(AddressOf OnInfoMessage)
Dim SQLResult As IAsyncResult = SQL.BeginExecuteNonQuery()
Try
Catch e As Exception
MessageBox.Show(e.Message)
End Try
SQL.EndExecuteNonQuery(SQLResult)
CompletedTask = True
DBConn.Close()
I have a feeling the only way I can get the text to update is through the addition of something in the DoWork() portion of the Backgroundworker but cannot figure out what exactly. Can the OnInfoMessages be called at will when an update is needed or is it only specific to where the Event Handler is?
As you can see in the image, It's during the actual "restoration" that the tool would normally report which transaction log has been restored based off of the SQL Message received but it remains blank...
Thank you once again!
EDIT: My progress update for my background worker is set up correctly, I just am having trouble calling events from the OnInfoMessage Sub as a progress update which is where gets the next line from SQL.
Make sure you use the backgroundworker properly. In the "doWork" there must not have any line of code that affect the UI(UserInterface) as this thread isn't the main thread of the application. If it does, it will either crash or do nothing.
So if you need to change the UI from the background worker you will need to tell the main thread to do so from the "DoWork" method. This can be achieved by hooking a callback to event "ProgressChanged" of your backgroundWorker object.
' This event handler updates the UI. '
Private Sub backgroundWorker1_ProgressChanged( _
ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _
Handles backgroundWorker1.ProgressChanged
Me.txtTRNStatus.Text = e.UserState.ToString()//Update UI
End Sub
From the "DoWork" method, raise this event like shown below to tell main thread to update UI. Use the second parameter to pass whatever is needed by main thread to do so.
Private Sub backgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs)
' This method will run on a thread other than the UI thread. '
' Be sure not to manipulate any Windows Forms controls created'
' on the UI thread from this method.'
Dim SomeObject As [Object]
//some stuff
backgroundWorker.ReportProgress(1, SomeObject)
//some stuff
backgroundWorker.ReportProgress(2, SomeObject)
End Sub
See MSDN for more detail on backgroundworker usage.
Or you could just use "invoke" like #Mark suggested in the comment. This tell main thread execute a sub. As long as the UI update is done by the main thread you will be fine.

Updating progress bar WPF Visual studio

So my issue may sound familiar, I simply want to update a progress bar concurrently with the data that is being processed in my program.
I have two windows: StartupWindow and UpdateWindow.
The Application begins by creating the startup window, the user will push a button to open the UpdateWindow. The following is the code for the button:
Private Sub btnUpdateClick(sender As Object, e As RoutedEventArgs) Handles btnUpdate.Click
Dim updateWindow As New UpdateWindow
updateWindow.ShowDialog()
btnUpdate.IsEnabled = False
End Sub
With this code I got the error most other people do: "The calling thread cannot access this object because a different thread owns it."
Private Sub updateDevice()
Dim currPercent As Integer
currPercent = ((sentPackets / totalPacketsToWrite) * 100)
progressLabel.content = currPercent.ToString + "%"
pb.Maximum = totalPacketsToWrite
pb.Value = sentPackets
If sentPackets < totalPacketsToWrite Then
'....update....
Else
MsgBox("Device now has update stored on flash!")
'...close everything up
End If
End Sub
Here are the three things I have tried so far:
Use Invoke/Dispatcher/Delegate to try and seem to only be able to put the update in a different thread's queue? Can't seem to pause other the other threads to update the UI either...
Implement the BackgroundWorker Class and use report progress from it, this worked but I could only update the progress bar under bw.DoWork I make a lot of calls and have responses from external devices so it would be difficult to put all my code under one function.
I read somewhere that since this was the second window created (called from the original) I would have issues with it. So I took someone's solution and tried to create and entire new thread when the 'update' button was pressed ie.:
Something to note is that I added a 'cancel' button:
Private Sub buttonStart_Click(sender As Object, e As RoutedEventArgs) Handles btnStart.Click
btnStart.IsEnabled = False
btnStart.Visibility = Windows.Visibility.Hidden
btnCancel.IsEnabled = True
btnCancel.Visibility = Windows.Visibility.Visible
beginUpdate()
End Sub
Private Sub buttonCancel_Click(sender As Object, e As RoutedEventArgs) Handles btnCancel.Click
btnStart.IsEnabled = True
btnStart.Visibility = Windows.Visibility.Visible
btnCancel.IsEnabled = False
btnCancel.Visibility = Windows.Visibility.Hidden
'TODO MAKE IT CANCEL
End Sub
And every time I clicked the update/cancel button the progress bar would update. Every time I pressed the button it refreshed the progress bar to the current completion. I'm quite puzzled, I am able to update user interfaces if it is just one window... but if the user pushes a button to call a new window I cannot update anything in the second window. Any suggestions?
EDIT:
I ended up making global variables that updated in my long code. Than ran backgroundworker before I did anything else and it ran asynchronous to my process, updating the progress bar:
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
While finished = False
Threading.Thread.Sleep(50)
bw.ReportProgress(currPercent)
End While
End Sub
The start of my code was simple - like so:
Private Sub buttonStart_Click(sender As Object, e As RoutedEventArgs) Handles btnStart.Click
bw.RunWorkerAsync()
beginUpdate()
End Sub
First things first... get the ProgressBar working: For this part, please read my answer to the Progress Bar update from Background worker stalling question here on Stack Overflow. Now, let's assume that you've got the basic update of a ProgressBar working... next, you want to be able to cancel the work. It is a simple matter to update the DowWork method accordingly:
private void DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i <= 100; i++)
{
if (IsCancelled) break; // cancellation check
Thread.Sleep(100); // long running process
backgroundWorker.ReportProgress(i);
}
}
So all you need to do to cancel the long running process and the ProgressBar update is to set the IsCancelled property to true.

Linq-to-SQL SubmitChanges not updating physical database

I am new here and only signed up because I desperately need the solution to my problem. I have a VB.NET project that is using a local .MDF file that I have imported into my project. The primary keys are intact so I know that isn't my issue.
I am using Linq-to-SQL to query the database and make changes. On my main form I use a Data Grid View that is populated by a query. I am having an issue with my insert where the query works fine and the DGV gets updated with newly created data, but when I call SubmitChanges() the changes do not get saved to the actual physical database. I am not sure what I am missing or doing wrong.
Here is my code to better assist your educated answer.
Public Class frmCell_Leader
'Reference DataContext
Private db As New MarquardtClassesDataContext()
Private Sub frmCell_Leader_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
FillProductionGrid()
PrepareInsertFields()
End Sub
Private Sub FillProductionGrid()
''''''''''''''''''''''''''''''''''
'E.Salce Last Modified 11.29.2012
'Comments: Working
''''''''''''''''''''''''''''''''''
'Populate DGV
Dim query = From marq In db.tblProductivities
Select
marq.PartNum, marq.Produced, marq.Packed, marq.Issue, marq.StationNum, _
marq.PlanDownTime, marq.UnplannedDownTime, marq.CurDate
dgvProduction.DataSource = query
End Sub
Private Sub PrepareInsertFields()
''''''''''''''''''''''''''''''''''
'E.Salce Last Modified 11.29.2012
'Comments: Working
''''''''''''''''''''''''''''''''''
'Fill Planned Downtime Combo Box
Dim PlannedQuery = From planned In db.tblPlannedDownTimes
Select planned.Plan_ID, planned.Description, planned.Active
Where Active = True
cboPlanned.DataSource = PlannedQuery
cboPlanned.DisplayMember = "Description"
cboPlanned.ValueMember = "Plan_ID"
'Fill UnPlanned Downtime Combo Box
Dim UnPlannedQuery = From unplanned In db.tblUnplannedDownTimes
Select unplanned.Unplan_ID, unplanned.Description, unplanned.Active
Where Active = True
cboUnplanned.DataSource = UnPlannedQuery
cboUnplanned.DisplayMember = "Description"
cboUnplanned.ValueMember = "UnPlan_ID"
End Sub
Private Sub btnAdd_Click(sender As System.Object, e As System.EventArgs) Handles btnAdd.Click
'Add the new Production to database
'Create an productivity object
Dim marqpro As New tblProductivity With {
.PartNum = CShort(txtPartNumber.Text),
.Produced = CShort(txtProduced.Text),
.Packed = CShort(txtPacked.Text),
.Issue = txtIssue.Text,
.PlanDownTime = CShort(cboPlanned.SelectedValue),
.UnplannedDownTime = CShort(cboUnplanned.SelectedValue),
.CurDate = Date.Today}
'insert newly created data
Try
'Submit changes too database
db.tblProductivities.InsertOnSubmit(marqpro)
db.SubmitChanges()
db.Refresh(Data.Linq.RefreshMode.KeepCurrentValues, marqpro)
'Refresh DGV
FillProductionGrid()
MessageBox.Show("Data added successfully")
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub
End Class
Perhaps you have an .mdf file in your project that is getting copied to the bin/debug folder everytime you build.
Why isn't my SubmitChanges() working in LINQ-to-SQL?

asynchronous threads and anonymous delegates

Ok, now I can use 2 techniques to launch my threads: Dispatcher and BackgroundWorker.
Dispatcher:
' I launch the asynchronous process
Dim a As New Action(AddressOf operazioneLunga)
a.BeginInvoke(Nothing, Nothing)
' I update the GUI passing some parameters
Dim a As New Action(Of Integer, String)(AddressOf aggiorna_UI)
Me.Dispatcher.BeginInvoke(DispatcherPriority.Normal, a, 5, "pippo")
BackgroundWorker:
Private bw As BackgroundWorker = Nothing
Private Sub initial()
bw = New BackgroundWorker
AddHandler bw.DoWork, AddressOf longOp
AddHandler bw.RunWorkerCompleted, AddressOf endBGW
bw.RunWorkerAsync ()
End Sub
Private Sub longOp(ByVal sender As Object, ByVal e As DoWorkEventArgs)
Dim l As List(Of miaClasse2) = <Long Operation ...>
e.Result = l
End Sub
Private Sub endBGW(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
Dim l As List(Of miaClasse2) = e.Result
Dim be As BindingExpression = BindingOperations.GetBindingExpression(mioDatagrid, DataGrid.ItemsSourceProperty)
Dim m As miaClasse1 = DirectCast(be.DataItem, miaClasse1)
m.GetData (l)
mioDatagrid.UpdateLayout()
mioDatagrid.ScrollIntoView (mioDatagrid.Items(0))
RemoveHandler bw.DoWork, AddressOf massiccia
RemoveHandler bw.RunWorkerCompleted, AddressOf fineBGW
bw.Dispose()
End Sub
I don't know what is better, but I think I'll use BackgroundWorker, because I suppose there are other argouments about Dispatcher I have to know and I don't feel safe.
Pileggi
My previous post:
Hi everyone!
My application is in WPF / Vb framework 3.5 SP1. I need to execute some methods on asynchronous threads. I know this way:
Private Delegate Sub dMassiccia()
Private Delegate Sub dAggiornaUI()
Private Sub iniziale()
Dim massicciaTemp As New dMassiccia(AddressOf massiccia)
massicciaTemp.BeginInvoke(Nothing, Nothing)
End Sub
Private Sub massiccia()
'long operations...
Me.Dispatcher.BeginInvoke(DispatcherPriority.Normal, _
New dAggiornaUI(AddressOf aggiornaUI))
End Sub
Private Sub aggiornaUI()
'update the UI...
End Sub
But in this way I have to declare a delegate for every mothod I want to launch on an asynchronous thread, and it's very uncomfortable. I have a lot of method to launch in this way. I know there are the anonymous delegates, but I don't know how to use them in this case.
Can you help me?
Pileggi
PS. Other information: in this moment I don't need to lookup the status of the process launched in the asynchronous thread. The long operations are some requests to a webservice that can take some seconds every time. There is no problem for the number of threads, because I limit the possibilities for the user to start new threads until one of them is finished. I need the asyncronous threads, among other reasons, because I don't wont to block the application, I want to replace the mouse cursor with a user-control, etc..
What is it that you're trying to do, that requires you to launch all of these threads? It looks like you're creating the secondary thread just to be able to do GUI updates.
First of all, if you have to create a lot of threads, then you run the risk of running out of available threads pretty quickly. I thought the max was only 64, but the documentation says 250 per process, and it's also settable via GetMaxThreads and SetMaxThreads. Regardless, you need to decide if using the ThreadPool threads (which is what's used when you use BeginInvoke/EndInvoke) is appropriate for you.
How long do your GUI updates take? Are they going to run the entire duration of your application? Can you use a regular thread instead? Look into using a BackgroundWorker for GUI updates if you just need to update status information periodically. In some cases, even DispatcherTimer might do the trick. It just depends on what you want to do.
You also don't show all of your code, but in what's posted, EndInvoke is not called. If you do this and end up throwing an exception, you won't be able to catch it and handle the error properly.

Resources