我如何防御性编码以防止随机引用 "Table 0" 和空值?
How can I defensively code against randomly referencing "Table 0" and null values?
我正在检索中等数量的数据并对其进行处理 - 没有什么特别之处。起初奇怪的是,对于某些数据集,它工作正常,而对于其他数据集,我收到以下错误消息:
这个错误信息似乎完全是胡说八道(误导,无论如何),因为失败集的数据不比成功集多,所以它不应该花更长的时间来 运行 "bad" 数据比较好。
在那之后出现的其他错误消息或许更具有启发性:
注意:我得到这些冗长的错误对话框,而不是更简洁的对话框,因为您可以在下面代码的 catch 块中看到调试代码。
所以很明显,这并不是真正的 "too much data" 问题,正如最初的错误信息所表明的那样。这也不是 "missing data" 问题,因为如果我查询 2014 年 12 月到 2015 年 12 月的数据(下个月没有数据),它 运行 没问题 - 它只是 returns 2015年12月全0;所以这一定是 "bad (not just missing) data" 问题。我如何确定什么是坏数据并防御性地防止它破坏应用程序的 运行?
第 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 行(在最后一条错误消息中引用)是此处的第一行代码:
private String GetContractedItemsTotal()
{
var allContractRecords = _itemsForMonthYearList.Where(x => x.ContractItem);
var totalContractItemPurchases = allContractRecords.Sum(x => x.TotalPurchases);
return totalContractItemPurchases.ToString("C");
}
是什么导致此代码有时因 "Cannot find Table 0" 和 "Value cannot be null" 异常而崩溃?或者更重要的是,当值为 null 时,如何防止它造成如此严重的破坏?
更多上下文:
_itemsForMonthYearList 定义如下:
private List<ItemsForMonthYear> _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<ItemsForMonthYear>();
}
_itemsForMonthYearList.Add(ifmy);
正如 jmcilhinney 所建议的,调整 CommandTimeout 值似乎是 ticket/done 的诀窍。
我最初将 SqlCommand 的 CommandTimeout 值设置为 300(5 分钟),但是我得到了 "Context Switch Deadlock occurred." 所以我将它减少到 120(2 分钟),这似乎或多或少 "sweet spot" 对我来说。我确实在几次测试中得到了 "Timeout expired" 一次,但是当我重试相同的精确范围时,它第二次成功完成,所以我猜它只是 "one of those things" - 120 有时是不够的超时,但 300 显然太多了。 IOW,这种在太少和太多之间的平衡似乎并不 "an exact science."
我正在检索中等数量的数据并对其进行处理 - 没有什么特别之处。起初奇怪的是,对于某些数据集,它工作正常,而对于其他数据集,我收到以下错误消息:
这个错误信息似乎完全是胡说八道(误导,无论如何),因为失败集的数据不比成功集多,所以它不应该花更长的时间来 运行 "bad" 数据比较好。
在那之后出现的其他错误消息或许更具有启发性:
注意:我得到这些冗长的错误对话框,而不是更简洁的对话框,因为您可以在下面代码的 catch 块中看到调试代码。
所以很明显,这并不是真正的 "too much data" 问题,正如最初的错误信息所表明的那样。这也不是 "missing data" 问题,因为如果我查询 2014 年 12 月到 2015 年 12 月的数据(下个月没有数据),它 运行 没问题 - 它只是 returns 2015年12月全0;所以这一定是 "bad (not just missing) data" 问题。我如何确定什么是坏数据并防御性地防止它破坏应用程序的 运行?
第 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 行(在最后一条错误消息中引用)是此处的第一行代码:
private String GetContractedItemsTotal()
{
var allContractRecords = _itemsForMonthYearList.Where(x => x.ContractItem);
var totalContractItemPurchases = allContractRecords.Sum(x => x.TotalPurchases);
return totalContractItemPurchases.ToString("C");
}
是什么导致此代码有时因 "Cannot find Table 0" 和 "Value cannot be null" 异常而崩溃?或者更重要的是,当值为 null 时,如何防止它造成如此严重的破坏?
更多上下文:
_itemsForMonthYearList 定义如下:
private List<ItemsForMonthYear> _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<ItemsForMonthYear>();
}
_itemsForMonthYearList.Add(ifmy);
正如 jmcilhinney 所建议的,调整 CommandTimeout 值似乎是 ticket/done 的诀窍。
我最初将 SqlCommand 的 CommandTimeout 值设置为 300(5 分钟),但是我得到了 "Context Switch Deadlock occurred." 所以我将它减少到 120(2 分钟),这似乎或多或少 "sweet spot" 对我来说。我确实在几次测试中得到了 "Timeout expired" 一次,但是当我重试相同的精确范围时,它第二次成功完成,所以我猜它只是 "one of those things" - 120 有时是不够的超时,但 300 显然太多了。 IOW,这种在太少和太多之间的平衡似乎并不 "an exact science."