繁体   English   中英

Linq扩展方法和错误处理

[英]Linq Extension Methods and Error Handling

我有以下C#代码......

    // We're essentially pivoting the data, using LINQ's GroupBy.
    var pivotedOperands = Operands.GroupBy(o => new { outputid = (Guid)o[DETAILS_OUTPUTID], unitid = o[DETAILS_UNITID] })
        .Select(g => new
        {
            PivotKey = g.Key,
            c1 = g.Where(x => (int)x[DETAILS_OV_SEQUENCEID] == 1).Sum(x => double.Parse(x[DETAILS_VALUE].ToString())),
            r1 = g.Where(x => (int)x[DETAILS_OV_SEQUENCEID] == 2).Sum(x => double.Parse(x[DETAILS_VALUE].ToString())),
            a1 = g.Where(x => (int)x[DETAILS_OV_SEQUENCEID] == 3).Sum(x => double.Parse(x[DETAILS_VALUE].ToString()))
        });

它获取操作数中的数据(这是一个List对象)并使用GroupBy()扩展方法对数据执行数据透视。 基本上c1,r1和a1都是不同DataRow对象中的值,序列ID分别为1,2和3。 (如果有必要的话,我可以进一步拘留,但我认为不会。)

所以有时c1的值可能是空的。 (它不应该,但是错误在这个过程中不时发生在上游。)如果c1不是数值,double.Parse()调用将引发异常。 没关系。 这是我的问题。 例如,如果操作数对象包含9个行,这些行将被转换为3行,并且这9个值中的一个不是数字,是否可以确定哪个DataRow对象引发了异常?

示例:如果操作数包含SequenceID和Value的以下值...

OutputID UnitID SequenceID Value
A        1      1          '0'
A        1      2          '0'
A        1      3          '0'
A        2      1          ''
A        2      2          '0'
A        2      3          '0'
B        1      1          '0'
B        1      2          '0'
B        1      3          '0'

...当我尝试通过double.Parse()方法为我的数据集的第4行处理空字符串时,我们将得到“输入字符串格式不正确”异常。 我想向用户提出一个友好的例外,告诉他们哪一行是问题; 不仅仅是这组数据中存在某个问题。 是否有可能确切地确定导致异常的原因?

如果在Visual Studio中创建新的C#控制台应用程序并将以下代码转储到Main方法中,则可以重现我的问题。

        // Create a DataTable so that we can easily create new DataRows to add to our List.
        DataTable dt = new DataTable();
        DataColumn col = new DataColumn();
        col.DataType = System.Type.GetType("System.String");
        col.ColumnName = "OutputID";
        dt.Columns.Add(col);

        col = new DataColumn();
        col.DataType = System.Type.GetType("System.Int32");
        col.ColumnName = "UnitID";
        dt.Columns.Add(col);

        col = new DataColumn();
        col.DataType = System.Type.GetType("System.Int32");
        col.ColumnName = "SequenceID";
        dt.Columns.Add(col);

        col = new DataColumn();
        col.DataType = System.Type.GetType("System.String");
        col.ColumnName = "Value";
        dt.Columns.Add(col);



        // Create the List and add our sample data
        List<DataRow> Operands = new List<DataRow>();

        DataRow dr = dt.NewRow();
        dr["OutputID"] = "A";
        dr["UnitID"] = "1";
        dr["SequenceID"] = 1;
        dr["Value"] = "0";
        Operands.Add(dr);

        dr = dt.NewRow();
        dr["OutputID"] = "A";
        dr["UnitID"] = "1";
        dr["SequenceID"] = 2;
        dr["Value"] = "0";
        Operands.Add(dr);

        dr = dt.NewRow();
        dr["OutputID"] = "A";
        dr["UnitID"] = "1";
        dr["SequenceID"] = 3;
        dr["Value"] = "0";
        Operands.Add(dr);

        dr = dt.NewRow();
        dr["OutputID"] = "A";
        dr["UnitID"] = "2";
        dr["SequenceID"] = 1;
        dr["Value"] = "";       // This should cause an error.
        Operands.Add(dr);

        dr = dt.NewRow();
        dr["OutputID"] = "A";
        dr["UnitID"] = "2";
        dr["SequenceID"] = 2;
        dr["Value"] = "0";
        Operands.Add(dr);

        dr = dt.NewRow();
        dr["OutputID"] = "A";
        dr["UnitID"] = "2";
        dr["SequenceID"] = 3;
        dr["Value"] = "0";
        Operands.Add(dr);

        dr = dt.NewRow();
        dr["OutputID"] = "B";
        dr["UnitID"] = "1";
        dr["SequenceID"] = 1;
        dr["Value"] = "0";
        Operands.Add(dr);

        dr = dt.NewRow();
        dr["OutputID"] = "B";
        dr["UnitID"] = "1";
        dr["SequenceID"] = 2;
        dr["Value"] = "0";
        Operands.Add(dr);

        dr = dt.NewRow();
        dr["OutputID"] = "B";
        dr["UnitID"] = "1";
        dr["SequenceID"] = 3;
        dr["Value"] = "0";
        Operands.Add(dr);

        // Now pivot the data
        try
        {
            var pivotedOperands = Operands.GroupBy(o => new { outputid = o[0], unitid = o[1] })
                .Select(g => new
                {
                    PivotKey = g.Key,
                    c1 = g.Where(x => (int)x[2] == 1).Sum(x => double.Parse(x[3].ToString())),
                    r1 = g.Where(x => (int)x[2] == 2).Sum(x => double.Parse(x[3].ToString())),
                    a1 = g.Where(x => (int)x[2] == 3).Sum(x => double.Parse(x[3].ToString()))
                });

            foreach (var o in pivotedOperands)
            {
                Console.WriteLine(string.Format("c1 = {0}; r1 = {1}; a1 = {2}", o.c1, o.r1, o.a1));
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }

        Console.WriteLine("Done.");
        Console.ReadLine();

根据您希望信息的显示方式,您可以更改结果类型以考虑失败的可能性,也可以捕获有关异常的上下文信息并在其中引发包含更多信息的新异常。

无论哪种情况,都不要害怕使用辅助方法。 例如,假设您通过创建如下方法来摆脱选择器中的重复代码:

string GetSumOrErrorMessage(int idToMatch, IEnumerable<DataRow> dataRow)
{
    try
    {
        var sum = dataRow.Where(x => (int)x[2] == idToMatch).Sum(x => double.Parse(x[3].ToString()));
        return sum.ToString();
    }
    catch (Exception)
    {
        return "Error happened here"; // or something more specific
    }
}

现在您可以像这样更改查询:

    var pivotedOperands = Operands.GroupBy(o => new { outputid = o[0], unitid = o[1] })
        .Select(g => new
        {
            PivotKey = g.Key,
            c1 = GetSumOrErrorMessage(1, g),
            r1 = GetSumOrErrorMessage(2, g),
            a1 = GetSumOrErrorMessage(3, g)
        });

你的输出变成:

 c1 = 0; r1 = 0; a1 = 0 c1 = Error happened here; r1 = 0; a1 = 0 c1 = 0; r1 = 0; a1 = 0 

如果您喜欢这种模式,而不是只返回一个string您可能需要查看可以帮助解决此问题的专用Monadic类型。 例如,您可以创建一个在操作成功时具有通用值的类,或者在不操作时创建错误消息。 您可以创建各种扩展方法和帮助程序,以便更容易处理,类似于我的CallMeMaybe库允许您尝试解析值,但只是在解析失败时返回空的Maybe<> (例如, Maybe.From(x[3].ToString()).ParseInt64().Select(i => i.ToString()).Else("Error happened here") )。

或者,如果您实际上想要在输入错误时暂停,但仍想知道错误输入的位置,则可以捕获并抛出:

double GetSum(int idToMatch, IGrouping<object, DataRow> dataRows)
{
    try
    {
        return dataRows.Where(x => (int)x[2] == idToMatch).Sum(x => double.Parse(x[3].ToString()));
    }
    catch (Exception e)
    {
        throw new Exception($"Failure when matching {idToMatch} with group {dataRows.Key}", e);
    }
}

...

    var pivotedOperands = Operands.GroupBy(o => new { outputid = o[0], unitid = o[1] })
        .Select(g => new
        {
            PivotKey = g.Key,
            c1 = GetSum(1, g),
            r1 = GetSum(2, g),
            a1 = GetSum(3, g)
        });

输出:

 c1 = 0; r1 = 0; a1 = 0 Failure when matching 1 with group { outputid = A, unitid = 2 } 

您尝试使用TryParse来绕过异常。 如果TryParse为false,则默认为零( 0

.Sum(x => { 
    double value = 0; 
    return double.TryParse(x[DETAILS_VALUE].ToString(), out value) ? value : 0; 
}) 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM