Как я могу защитить код от случайной ссылки на "Таблица 0" и нулевые значения?

Я получаю небольшое количество данных и обрабатываю их - ничего уникального там нет. Сначала было странно, что с некоторыми наборами данных все работало нормально, а с другими я получил следующее сообщение об ошибке:

Это сообщение об ошибке, похоже, является полной фигней (впрочем, вводит в заблуждение, во всяком случае), потому что с ошибочным набором нет больше данных, чем с успешным набором, и поэтому для запуска "плохих" данных не требуется больше времени. чем хорошо.

Возможно, более поразительными являются и другие ошибочные сообщения, которые появляются после этого:

Примечание: я получаю эти подробные диалоги с ошибками, а не что-то более сжатое из-за кода отладки, который вы можете увидеть в блоке catch кода ниже.

Таким образом, кажется очевидным, что на самом деле это не проблема "слишком большого объема данных", как указывает первоначальное сообщение об ошибке. Это также не проблема "отсутствующих данных", потому что, если я запрашиваю данные с декабря 2014 года по декабрь 2015 года (для которых в следующем месяце нет данных), он работает нормально - он просто возвращает все 0 в декабре 2015 года; таким образом, это должна быть проблема "плохих (не просто отсутствующих) данных". Как я могу определить, что такое плохие данные, и защититься, чтобы они не испортили работу приложения?

Строка 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");
}

Что может вызывать сбой этого кода с исключениями "Не удается найти таблицу 0" и "Значение не может быть пустым"? Или, что более важно, как я могу предотвратить хаос, если значение равно нулю?

Еще немного контекста:

_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);

1 ответ

Решение

Как подсказывает jmcilhinney, подстройка значения CommandTimeout, похоже, была идеальным решением.

Первоначально я установил значение CommandTimeout SqlCommand 300 (5 минут), но после этого я получил "Произошла блокировка переключения контекста". Затем я уменьшил его до 120 (2 минуты), и это, кажется, более или менее "сладкое пятно" для меня. Я получил "Timeout expired" один раз из нескольких тестов, но когда я повторил тот же точный диапазон, он успешно завершился во второй раз, так что я думаю, что это просто "одна из тех вещей" - 120 иногда не будет достаточно тайм-аут, но 300, очевидно, слишком много. Таким образом, этот баланс между слишком маленьким и слишком большим не кажется "точной наукой".

Другие вопросы по тегам