LINQ方法链接和粒度错误处理

时间:2012-06-07 10:15:24

标签: c# linq chaining method-chaining

我有一个方法可以通过方法链非常巧妙地编写:

return viewer.ServerReport.GetParameters()
    .Single(p => p.Name == Convention.Ssrs.RegionParamName)
    .ValidValues
    .Select(v => v.Value);

但是,我希望能够在每个点进行一些检查,因为如果任何链式方法返回意外结果,我希望提供有用的诊断信息。

为实现这一目标,我需要打破所有链接并使用if块跟踪每个调用。它使代码的可读性降低了很多。

理想情况下,我希望能够编织一些链式方法调用,这些调用允许我在每个点处理意外结果(例如,如果第一个方法返回一个空集合,则抛出一个有意义的异常,例如new ConventionException("The report contains no parameter") )。任何人都可以建议一种简单的方法来实现这样的事情吗?

修改

这是使用@ JeffreyZhao的答案的结果:

return viewer.ServerReport.GetParameters()
    .Assert(result => result.Any(), "The report contains no parameter")
    .SingleOrDefault(p => p.Name == Convention.Ssrs.RegionParamName)
    .Assert(result => result != null, "The report does not contain a region parameter")
    .ValidValues
    .Select(v => v.Value)
    .Assert(result => result.Any(), "The region parameter in the report does not contain any valid value");

3 个答案:

答案 0 :(得分:7)

也许你可以使用这种方法。

static T Check<T>(this T value)
{
    if (...) throw ...;

    return value;
}

然后:

xxx.Single(...).Check().Select(...).Check()...

<强>更新

你甚至可以:

static T Validate<T>(this T value, Func<T, bool> validate, string errorMessage)
{
    if (!validate(value)) 
        throw new ValidationFailedException(errorMessage);

    return value;
}

然后:

xxxx.Single()
    .Validate(v => v > 0, "Must be greater than zero")
    .NextStep()
    .Validate(...);

答案 1 :(得分:1)

您可以使用本地变量在单独的步骤中轻松拆分流程:

var result1 = viewer.ServerReport.GetParameters();
var result2 = result1.Single(p => p.Name == Convention.Ssrs.RegionParamName);
var result3 = result2.ValidValues;
var result4 = result3.Select(v => v.Value);
return result4;

现在,您可以在步骤之间进行任何检查。

但请注意,某些结果实际上并没有做任何工作。例如,最后一步不生成列表作为结果,它生成一个从ValidValues读取的枚举,因此当您使用结果时,该步骤中的任何错误都会发生,而不是在此方法中。您可能希望在某些步骤结束时添加.ToList()以实现结果。

答案 2 :(得分:1)

考虑使用Code Contracts(例如,将后置条件添加到ServerReport.GetParameters以确保方法不返回空集合)。与编写自己的检查逻辑相比,它们允许以更优雅的方式做你想做的事。

class ReportParameter { }
class ServerReport
{
    public ReportParameter[] GetParameters()
    {
        Contract.Ensures(Contract.Result<ReportParameter[]>() != null && Contract.Result<ReportParameter[]>().Length > 0,
            Resource1.Oops);

        // here's some logic to build parameters array...
        return new ReportParameter[0];
    }
}

用法:

// Oops! I need at least one parameter!
var parameters = new ServerReport().GetParameters();