2014-07-01 23 views
0

以下是一个将Insert放入数据库表的方法。我在BackGroundWorker线程的DoWork()内调用此方法。它显然引发了我的“跨线程操作无效......”错误。据我所知,我可以在UI控件上使用Invoke()方法,如果那些要在DoWork()内访问。但是这是否意味着以下每个UI控件都应该被调用?有没有更好的方法来实现这一目标?这是否意味着必须在每个UI控件上调用Invoke()方法?

private void AddToOccupations() 
    { 
     if (dataGridViewSearch.SelectedRows.Count > 0) 
     { 
      foreach (DataGridViewRow datarow in dataGridViewSearch.SelectedRows) 
      { 
       try 
       { 
        AllocationModel occupation = new AllocationModel() 
        { 
         User_ID = userID, 
         Merge_Status = (int)((MergeStatus)Enum.Parse(typeof(MergeStatus), cmbMergeStatus.SelectedItem.ToString())), 
         Start_Date = dateTimePickerFROMDate.Value, 
         Seat_Type = datarow.Cells[2].Value.ToString(), 
         Occupation_Status = cmbStatus_Type.SelectedItem.ToString(), 
         Session = datarow.Cells[3].Value.ToString(), 
         Seat_Number = (Int32)datarow.Cells[0].Value, 
         Number_of_Guests = (Int32)datarow.Cells[1].Value 
        }; 

        // Call the service method 
        var success = this.allocationService.AddToOccupation(occupation); 

        if (success) 
        { 
         // display the message box 
         MessageBox.Show(
          Resources.Add_Occupation_Success_Message, 
          Resources.Add_Occupation_Success_Title, 
          MessageBoxButtons.OK, 
          MessageBoxIcon.Information); 

         // Reset the screen 
         //this.Reset(); 

         DoCurrentFreeSearch(); 
        } 
        else 
         MessageBox.Show("No Records Inserted!"); 
       } 
       catch (FormatException ex) 
       { 
        errorMessage = "Insert Error: \n"; 
        errorMessage += ex.Message; 
        MessageBox.Show(errorMessage); 
       } 
      } 
     } 
     else 
      MessageBox.Show("Warning! No Row is selected..."); 
    } 

回答

1

题目问题的一般答案是:是的。

如果一个后台工作线程需要访问一个UI项目,它只能通过Invoking这样做来实现。

通过以解耦方式访问其DataSource,可以很好地解决从BW将数据放入DGV的问题。我只是做了一个小测试,看看我的原帖是否有效,而且看起来很好。

所以,与Bitmap一样,您可以创建两个DataSources作为属性;一个从后台工作人员填写,一个用作DGV的Datasource

public List<SeatData> theSeats_buffer { get; set; } 
public List<SeatData> theSeats_DS { get; set; } 

在BW线程的DoWork()通过调用合适的函数void或BOOL getSeatData()填写theSeats_buffer列表,当你的工作量做了你的数据传递到theSeats_DS,也许是这样的:

theSeats_DS= new List<Cols>(); theSeats_DS.AddRange(theSeats_buffer); 

同样,这个操作必须由可能锁定接收一览表theSeats_DS进行线程安全的。

由于DataSource已被重新创建,因此应在bw_RunWorkerCompleted事件中重新分配;我做了正确的无效显示面板1:

private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    if ((e.Cancelled == true)) 
    { this.tbProgress.Text += "Cancelled!"; } 
    else if (!(e.Error == null)) 
    { this.tbProgress.Text += ("Error: " + e.Error.Message); } 
    else 
    { panel1.Invalidate(); DGV.DataSource = theSeats_DS; } 
} 

关于DB插入,我不知道它是如何相关的。这个答案只是关于从异地获取数据并将它们发送到UI。

将数据从DGV获取到数据库中并不会在BW线程中发生,至少不会在传入更改时触发。如果您使用DGV进行输入,并发将是一个问题!如果您试图预留的座位实际上是在您按下输入按钮时截取的,那已经够糟糕的了。但这是无法预防的。但是,您需要确保输入未被输入更改消灭.. OTOH,信号会很好..

并发是棘手的!

-1

由于控件在UI线程上运行,而不是在后台工作线程上运行,所以需要调用它们的所有函数。

+0

这就是我所面对的显而易见的问题。如果我要改变上述方法(基于DML事务的方法的数量),这将会是代码中的巨大变化......您认为什么是替代方法? – bonCodigo

+0

如果您可以将C#5用于您的工作,那么这将成为异步/等待功能的理想候选者。它在代码中的侵入性要小得多。 – Emile

+0

@Emile darn,因为它是,我在4.0,它不会很快上山......现在我看到**为什么** Asynch/Await *首次亮相* ...照顾足以提供给我一个例如,你推荐(在网上)Asynch的例子像我的... – bonCodigo

2

您应该从GUI代码中分离出工作者代码。该数据网格上没有DataSource吗?你基本上应该首先从网格中获取你需要的数据(选定的行),并从GUI代码(按钮点击或其他)将它们传递给后台工作人员。然后您可以通过BackgroundWorker.ReportProgress(它在GUI线程上执行进度事件)报告工作进度。

如果您无法完全将GUI代码与工作代码解耦,则必须使用Invoke。但是,从代码中不清楚为什么你需要这样做,ReportProgress应该足够了。

+0

这里是功能流程:添加按钮点击 - >验证方法代表(因为还有其他类型的插入) - >输入验证 - >相应的DML方法调用(例如AddToOccupations())因为您指出需要为了分离,我猜测输入验证级别,我将创建一个类来保存GUI输入数据。然后调用该对象的属性在这里传递值。 – bonCodigo

+0

@bonCodigo是的,这听起来像一个好地方。 – Luaan

相关问题