2014-10-02 56 views
1

我们最近开始研究一个新应用程序,该应用程序将用于非常有限带宽的环境中。因为我的设计师担心加载数据的时间。取消对存储过程的调用

目前我们有一个系统,如下所示:

一个ListView充满了出货量,点击出货显示了侧面板。 侧面板上有一个“Details”按钮,点击此按钮将进行存储过程调用以加载所选货件的详细信息。

现在在我们以前的应用程序中,这不是线程化的,加载时间很短以至于不会引起任何问题(位于内部网络上)。然而,由于新的限制,我们现在将调用过程调用并显示加载动画,但是我的设计人员想要添加取消加载的功能。

这是我卡住的地方,因为按钮调用我的静态库(LoadDetails)上的单个方法我看不到一种方法来取消此加载。除此之外,用户可以选择货件点击细节,然后选择第二个,第三个货件点击所有这些细节,并对多个线程加载数据进行后台处理。

资源库LoadDetails如下:

private static bool LoadDetails(int shipmentId) 
    { 
     DataConnection dbCon = null; 
     try 
     { 
      dbCon = ApplicationRoleService.EnableAppRole(); 
      if (dbCon.SqlConn == null) 
      { 
       return false; 
      } 

      SqlCommand cmd = new SqlCommand("LBN.sel_shipments_details"); 
      cmd.CommandType = CommandType.StoredProcedure; 
      cmd.Connection = dbCon.SqlConn; 

      SqlParameter paramShipmentId = new SqlParameter(); 
      paramShipmentId.ParameterName = "shipment_id"; 
      paramShipmentId.Direction = ParameterDirection.Input; 
      paramShipmentId.Value = shipmentId; 
      cmd.Parameters.Add(paramShipmentId); 

      SqlParameter paramReturn = new SqlParameter(); 
      paramReturn.ParameterName = "@return_value"; 
      paramReturn.Direction = ParameterDirection.ReturnValue; 
      cmd.Parameters.Add(paramReturn); 

      SqlDataAdapter adp = new SqlDataAdapter(cmd); 
      DataSet ds = new DataSet(); 
      adp.Fill(ds); 

      if ((int)paramReturn.Value < 0) 
      { 
       //TODO: Log Error here. 
       log.Error("Get Shipment Details Failed."); 
      } 

      foreach (DataRow row in ds.Tables[0].Rows) 
      { 
       App.Current.Dispatcher.Invoke((System.Action)delegate 
       { 
        Shipment s = GetShipment(shipmentId); 
        s.ShipmentDetails(
          row["bankContact"] as string, 
          row["bankContactTel"] as string, 
          (row["containerCount"] == DBNull.Value) ? 0 : (int)row["containerCount"], 
          (row["sentToSiteComp"] == DBNull.Value) ? 0 : (int)row["sentToSiteComp"], 
          (row["arrivedAtSiteComp"] == DBNull.Value) ? 0 : (int)row["arrivedAtSiteComp"],               
          (row["sentToPortComp"] == DBNull.Value) ? 0 : (int)row["sentToPortComp"], 
          (row["depostRecComp"] == DBNull.Value) ? 0 : (int)row["depostRecComp"] 
         ); 
       }); 
      } 
      IoC.Get<IEventAggregator>().PublishOnUIThread(new ShipmentDetailsLoaded()); 
      return true; 
     } 
     catch (Exception ex) 
     { 
      log.Error("Unable to get ShipmentsList.", ex); 
      return false; 
     } 
     finally 
     { 
      ApplicationRoleService.CloseConnection(dbCon); 
     } 
    } 

我怎么可能去加入取消从主线程这种方法的能力吗?

+0

您正在为每个LoadDetails创建一个新线程?你不能使用thread.abort()吗?或者你想取消存储过程调用? – artm 2014-10-02 11:06:42

+0

如果是带宽瓶颈,那么最耗费资源的操作就是DataAdapter.Fill(你必须通过网读取所有数据);使它异步,并让它被取消请参阅http://stackoverflow.com/questions/2108917/sqldataadapter-fill-asynchronous-approach – 2014-10-02 11:07:29

+0

还有SqlCommand.Cancel – artm 2014-10-02 11:09:20

回答

2

没有可靠和安全的方法来强制取消.NET代码中的任何后台操作,除非在单独的进程中运行该操作并杀死进程(这不是通用解决方案)。特别是,如果该操作目前正在运行本机代码。

,你可以在这里做的最好的选择是:

  • 分割成数据块的巨量;
  • 跟随在你的代码优雅消除图案(见thisthis
  • 关于当前加载操作的结果给忘了,如果用户按下“取消”按钮(或做类似的东西,像离开当前记录和定位下一个)。

我不会依靠SqlCommand.CancelThread.Abort也不是一个选项。

+0

您可否详细说明为什么'Thread.Abort'和'SqlCommand.Cancel'是糟糕的选择? – 2014-10-02 12:25:20

+0

关于'Thread.Abort':http://stackoverflow.com/questions/1559255/whats-wrong-with-using-thread-abort关于'SqlCommand.Cancel':正如它在文档中所说的,'Cancel'只是*尝试* 取消。这种尝试*可能会成功(并且可能不会,并且有客观的原因)。此外,无法保证,“取消”将在某个时间结束,符合您的要求。 – Dennis 2014-10-02 12:30:44