使用Single和SingleOrDefault可以抛出更简洁的异常

Mat*_*hew 5 .net c# linq design-patterns exception

当调用SingleSingleOrDefaulton IEnumerable<T>,并且它有多个结果时,它会抛出InvalidOperationException.

虽然异常的实际消息是非常具有描述性的,但编写一个仅处理Single/ SingleOrDefaultcalls失败的情况的catch是有问题的.

public virtual Fee GetFeeByPromoCode(string promoCode)
{
    try
    {
        return _fees.SingleOrDefault(f => f.IsPromoCodeValid(promoCode));
    }
    catch (InvalidOperationException)
    {
        throw new TooManyFeesException();
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,如果IsPromoCodeValid还抛出一个InvalidOperationException,那么捕获的处理方式就变得模棱两可.

可以检查异常的消息,但我想避免这种情况,因为我发现它根据异常消息处理代码是脏的.

我目前的替代方案SingleOrDefault如下:

public virtual Fee GetFeeByPromoCode(string promoCode)
{
    var fees = _fees.Where(f => f.IsPromoCodeValid(promoCode)).ToList();

    if (fees.Count > 1)
    {
        throw new InvalidFeeSetupException();
    }

    return fees.FirstOrDefault();
}
Run Code Online (Sandbox Code Playgroud)

但是,这个代码比上面的代码要明显得多,此外,这会产生一个效率较低的查询(如果使用支持linq的ORM)而不是使用SingleOrDefault.

我也可以Take(2)用我的第二个例子来优化它,但这进一步模糊了代码的意图.

有没有办法做到这一点没有写我自己的扩展都IEnumerableIQueryable

Fra*_*tyx 2

这可以解决问题吗?

public virtual Fee GetFeeByPromoCode(string promoCode)
{
    try
    {
        return _fees.SingleOrDefault(f =>
            {
                try
                {
                    return f.IsPromoCodeValid(promoCode);
                }
                catch(InvalidOperationException)
                {
                    throw new PromoCodeException();
                }
            });
    }
    catch (InvalidOperationException)
    {
        throw new TooManyFeesException();
    }
}
Run Code Online (Sandbox Code Playgroud)