如何防御性地编码以随机引用“表0”和空值?

我正在检索适量的数据并对其进行处理 – 没有什么独特之处。 一开始很奇怪的是,有了一些数据集,它运行良好,而且与其他人一起,我得到了以下错误信息:

在此处输入图像描述

这个错误的消息似乎是完全的混合(但无论如何都是误导性的),因为失败的集合没有比成功的集合更多的数据,因此不应该再运行“坏”数据比好。

或许,在这之后出现的另一个错误信息是:

在此处输入图像描述

在此处输入图像描述

注意:我得到这些冗长的错误对话框,而不是更简洁的东西,因为您可以在下面的代码的catch块中看到调试代码。

所以很明显它并不是真正的“太多数据”问题,正如最初的错误消息所示。 这也不是“缺失数据”问题,因为如果我查询2014年12月到2015年12月的数据(即将到来的月份没有数据),它运行正常 – 它只返回2015年12月的全部0; 所以它必须是“坏(不仅仅是缺失)数据”问题。 如何确定坏数据是什么,防御性地阻止它破坏应用程序的运行?

涉及上述错误消息的第601行包含以下代码:

private void ReadData(string _unit, string monthBegin, string monthEnd, string beginYear, string endYear) { try { String dateBegin = UsageRptConstsAndUtils.GetYYYYMMDD(monthBegin, beginYear, true); String dateEnd = UsageRptConstsAndUtils.GetYYYYMMDD(monthEnd, endYear, false); DateTime dtBegin = UsageRptConstsAndUtils.DatifyYYYYMMDD(dateBegin); DateTime dtEnd = UsageRptConstsAndUtils.DatifyYYYYMMDD(dateEnd); DataTable dtUsage = SqlDBHelper.ExecuteDataSet("sp_ViewProductUsage_MappingRS", CommandType.StoredProcedure, new SqlParameter() { ParameterName = "@Unit", SqlDbType = SqlDbType.VarChar, Value = _unit }, new SqlParameter() { ParameterName = "@BegDate", SqlDbType = SqlDbType.DateTime, Value = dtBegin }, new SqlParameter() { ParameterName = "@EndDate", SqlDbType = SqlDbType.DateTime, Value = dtEnd } ); 

SqlDBHelper.ExecuteDataSet()是:

 public static DataTable ExecuteDataSet(string sql, CommandType cmdType, params SqlParameter[] parameters) { using (DataSet ds = new DataSet()) using (SqlConnection connStr = new SqlConnection(UsageRptConstsAndUtils.CPSConnStr)) using (SqlCommand cmd = new SqlCommand(sql, connStr)) { cmd.CommandType = cmdType; foreach (var item in parameters) { cmd.Parameters.Add(item); } try { cmd.Connection.Open(); new SqlDataAdapter(cmd).Fill(ds); } catch (SqlException sqlex) { for (int i = 0; i < sqlex.Errors.Count; i++) { var sqlexDetail = String.Format("From ExecuteDataSet(), SQL Exception #{0}{1}Source: {2}{1}Number: {3}{1}State: {4}{1}Class: {5}{1}Server: {6}{1}Message: {7}{1}Procedure: {8}{1}LineNumber: {9}", i + 1, // Users would get the fantods if they saw #0 Environment.NewLine, sqlex.Errors[i].Source, sqlex.Errors[i].Number, sqlex.Errors[i].State, sqlex.Errors[i].Class, sqlex.Errors[i].Server, sqlex.Errors[i].Message, sqlex.Errors[i].Procedure, sqlex.Errors[i].LineNumber); MessageBox.Show(sqlexDetail); } } catch (Exception ex) { String exDetail = String.Format(UsageRptConstsAndUtils.ExceptionFormatString, ex.Message, Environment.NewLine, ex.Source, ex.StackTrace); MessageBox.Show(exDetail); } return ds.Tables[0]; } } 

第396行(在上一个错误的msg中引用)是这里的第一行代码:

 private String GetContractedItemsTotal() { var allContractRecords = _itemsForMonthYearList.Where(x => x.ContractItem); var totalContractItemPurchases = allContractRecords.Sum(x => x.TotalPurchases); return totalContractItemPurchases.ToString("C"); } 

什么可能导致此代码有时崩溃“无法找到表0”和“值不能为空”exception? 或者更重要的是,当值为null时,如何防止它造成这样的破坏呢?

更多背景:

_itemsForMonthYearList的定义如下:

 private List _itemsForMonthYearList; 

..并填充如下:

 var ifmy = new ItemsForMonthYear(); int qty = Convert.ToInt32(productUsageByMonthDataRow["TotalQty"]); // TotalPrice as Decimal for calculation Decimal totPrice = Convert.ToDecimal(productUsageByMonthDataRow["TotalPrice"]); Decimal avgPrice = Convert.ToDecimal(productUsageByMonthDataRow["AvgPrice"]); String monthYear = productUsageByMonthDataRow["MonthYr"].ToString(); ifmy.ItemDescription = desc; ifmy.TotalPackages = qty; ifmy.TotalPurchases = totPrice; ifmy.AveragePrice = avgPrice; ifmy.monthYr = monthYear; ifmy.ContractItem = contractItem; // added 11/16/2016 if (null == _itemsForMonthYearList) { _itemsForMonthYearList = new List(); } _itemsForMonthYearList.Add(ifmy); 

正如jmcilhinney建议的那样,调整CommandTimeout值似乎已经成功了。

我最初使SqlCommand的CommandTimeout值为300(5分钟),但是我得到了“Context Switch Deadlock”。 所以我把它减少到120(2分钟),这似乎或多或少是我的“甜蜜点”。 我确实在几次测试中得到了“Timeout expired”一次,但是当我重复相同的确切范围时,它第二次成功完成,所以我猜它只是“其中一个” – 120有时候还不够超时,但300显然太多了。 IOW,这种平衡行为太少和太多似乎都不是“一门精确的科学”。